publ-aut.lua /size: 33 Kb    last modification: 2021-10-28 13:50
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
publ-aut
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
this module part of publication support
"
,
4
author
=
"
Hans Hagen, PRAGMA-ADE, Hasselt NL
"
,
5
copyright
=
"
PRAGMA ADE / ConTeXt Development Team
"
,
6
license
=
"
see context related readme files
"
7
}
8 9
if
not
characters
then
10
dofile
(
resolvers
.
findfile
(
"
char-def.lua
"
)
)
11
dofile
(
resolvers
.
findfile
(
"
char-ini.lua
"
)
)
12
end
13 14
local
lpeg
=
lpeg
15 16
local
type
,
next
,
tostring
,
tonumber
=
type
,
next
,
tostring
,
tonumber
17
local
concat
,
sortedhash
=
table
.
concat
,
table
.
sortedhash
18
local
utfsub
=
utf
.
sub
19
local
find
=
string
.
find
20
local
formatters
=
string
.
formatters
21 22
local
P
,
S
,
C
,
V
,
Cs
,
Ct
,
Cg
,
Cf
,
Cc
=
lpeg
.
P
,
lpeg
.
S
,
lpeg
.
C
,
lpeg
.
V
,
lpeg
.
Cs
,
lpeg
.
Ct
,
lpeg
.
Cg
,
lpeg
.
Cf
,
lpeg
.
Cc
23
local
lpegmatch
,
lpegpatterns
=
lpeg
.
match
,
lpeg
.
patterns
24
local
settings_to_hash
=
utilities
.
parsers
.
settings_to_hash
25 26
local
context
=
context
27
----- commands = commands
28 29
local
implement
=
interfaces
.
implement
30 31
local
publications
=
publications
32 33
local
datasets
=
publications
.
datasets
34
local
getcasted
=
publications
.
getcasted
35 36
local
allocate
=
utilities
.
storage
.
allocate
37 38
local
chardata
=
characters
.
data
39 40
local
trace_hashing
=
false
trackers
.
register
(
"
publications.authorhash
"
,
function
(
v
)
trace_hashing
=
v
end
)
41 42
local
expand_authors
=
false
directives
.
register
(
"
publications.prerollauthor
"
,
function
(
v
)
expand_authors
=
v
end
)
43 44
local
report
=
logs
.
reporter
(
"
publications
"
,
"
authors
"
)
45
local
report_cite
=
logs
.
reporter
(
"
publications
"
,
"
cite
"
)
46 47
local
v_last
=
interfaces
.
variables
.
last
48 49
-- local function makesplitter(separator)
50
-- return Ct { "start",
51
-- start = (Cs((V("outer") + (1-separator))^1) + separator^1)^1,
52
-- start = Cs(V("outer")) + (Cs((V("inner") + (1-separator))^1) + separator^1)^1,
53
-- outer = (P("{")/"") * ((V("inner") + P(1-P("}")))^0) * (P("}")/""),
54
-- inner = P("{") * ((V("inner") + P(1-P("}")))^0) * P("}"),
55
-- }
56
-- end
57 58
-- authorlist = { authorspec and authorspec and authorspec }
59
-- authorspec = composedname
60
-- authorspec = surnames, firstnames
61
-- authorspec = von, surnames, firstnames
62
-- authorspec = von, surnames, jr, firstnames
63
-- authorspec = von, surnames, jr, firstnames, initials
64 65
local
space
=
lpegpatterns
.
whitespace
66
local
comma
=
P
(
"
,
"
)
67
local
period
=
P
(
"
.
"
)
+
P
(
"
{.}
"
)
68
local
dash
=
P
(
"
-
"
)
+
P
(
"
{-}
"
)
69
local
firstcharacter
=
lpegpatterns
.
utf8byte
70
local
utf8character
=
lpegpatterns
.
utf8character
71
local
p_and
=
space
^
1
*
(
P
(
"
and
"
)
+
P
(
"
&&
"
)
+
P
(
"
++
"
)
)
*
space
^
1
72
local
p_comma
=
space
^
0
*
comma
*
space
^
0
73
local
p_space
=
space
^
1
74
local
p_shortone
=
C
(
(
utf8character
-
dash
-
period
)
^
1
)
75
local
p_longone
=
C
(
utf8character
)
*
(
1
-
dash
-
period
)
^
0
76 77
local
p_empty
=
P
(
"
{}
"
)
/
"
"
*
#
(
p_space
^
0
*
(
P
(
-1
)
+
P
(
"
,
"
)
)
)
78 79
local
andsplitter
=
Ct
{
"
start
"
,
80
start
=
(
Cs
(
(
V
(
"
inner
"
)
+
(
1
-
p_and
)
)
^
1
)
+
p_and
)
^
1
,
81
inner
=
P
(
"
{
"
)
*
(
(
V
(
"
inner
"
)
+
P
(
1
-
P
(
"
}
"
)
)
)
^
1
)
*
P
(
"
}
"
)
,
82
}
83 84
local
commasplitter
=
Ct
{
"
start
"
,
85
start
=
Cs
(
V
(
"
outer
"
)
)
+
(
p_empty
+
Cs
(
(
V
(
"
inner
"
)
+
(
1
-
p_comma
)
)
^
1
)
+
p_comma
)
^
1
,
86
outer
=
(
P
(
"
{
"
)
/
"
"
)
*
(
(
V
(
"
inner
"
)
+
P
(
1
-
P
(
"
}
"
)
)
)
^
1
)
*
(
(
P
(
"
}
"
)
*
P
(
-1
)
)
/
"
"
)
,
87
inner
=
P
(
"
{
"
)
*
(
(
V
(
"
inner
"
)
+
P
(
1
-
P
(
"
}
"
)
)
)
^
1
)
*
P
(
"
}
"
)
,
88
}
89 90
local
spacesplitter
=
Ct
{
"
start
"
,
91
start
=
Cs
(
V
(
"
outer
"
)
)
+
(
Cs
(
(
V
(
"
inner
"
)
+
(
1
-
p_space
)
)
^
1
)
+
p_space
)
^
1
,
92
outer
=
(
P
(
"
{
"
)
/
"
"
)
*
(
(
V
(
"
inner
"
)
+
P
(
1
-
P
(
"
}
"
)
)
)
^
1
)
*
(
(
P
(
"
}
"
)
*
P
(
-1
)
)
/
"
"
)
,
93
inner
=
P
(
"
{
"
)
*
(
(
V
(
"
inner
"
)
+
P
(
1
-
P
(
"
}
"
)
)
)
^
1
)
*
P
(
"
}
"
)
,
94
}
95 96
local
p_initial
=
p_shortone
*
period
*
dash
^
0
97
+
p_longone
*
(
period
+
dash
+
P
(
-1
)
)
98
local
initialsplitter
=
p_initial
*
P
(
-1
)
+
Ct
(
(
p_initial
)
^
1
)
99 100
local
optionsplitter
=
Cf
(
Ct
(
"
"
)
*
Cg
(
C
(
(
1
-
space
)
^
1
)
*
space
^
0
*
Cc
(
true
)
)
^
1
,
rawset
)
101 102
local
function
is_upper
(
str
)
103
local
first
=
lpegmatch
(
firstcharacter
,
str
)
104
local
okay
=
chardata
[
first
]
105
return
okay
and
okay
.
category
=
=
"
lu
"
106
end
107 108
-- local cleaner = Cs( ( P("{}")/"" + P(1) )^1 )
109 110
local
cache
=
allocate
(
)
-- 33% reuse on tugboat.bib
111
local
nofhits
=
0
112
local
nofused
=
0
113 114
publications
.
authorcache
=
cache
115 116
local
function
makeinitials
(
firstnames
)
117
if
firstnames
and
#
firstnames
>
0
then
118
local
initials
=
{
}
119
for
i
=
1
,
#
firstnames
do
120
initials
[
i
]
=
lpegmatch
(
initialsplitter
,
firstnames
[
i
]
)
121
end
122
return
initials
123
end
124
end
125 126
local
authormap
=
allocate
(
)
127
publications
.
authormap
=
authormap
128 129
local
prerollcmdstring
=
publications
.
prerollcmdstring
130 131
local
function
splitauthor
(
author
,
justsplit
)
132
local
detail
,
remapped
133
if
not
justsplit
then
134
detail
=
cache
[
author
]
135
if
detail
then
136
return
detail
137
end
138
remapped
=
authormap
[
author
]
139
if
remapped
then
140
report
(
"
remapping %a to %a
"
,
author
,
remapped
)
141
local
detail
=
cache
[
remapped
]
142
if
detail
then
143
cache
[
author
]
=
detail
144
return
detail
145
end
146
end
147
end
148
local
author
=
remapped
or
author
149
local
firstnames
,
vons
,
surnames
,
initials
,
juniors
,
options
150
if
expand_authors
and
find
(
author
,
"
\\btxcmd
"
)
then
151
author
=
prerollcmdstring
(
author
)
152
end
153
local
split
=
lpegmatch
(
commasplitter
,
author
)
154
local
n
=
#
split
155
detail
=
{
156
original
=
author
,
157
snippets
=
n
,
158
}
159
if
n
=
=
1
then
160
-- {First Middle von Last}
161
local
words
=
lpegmatch
(
spacesplitter
,
author
)
162
local
i
=
1
163
local
n
=
#
words
164
firstnames
=
{
}
165
vons
=
{
}
166
surnames
=
{
}
167
while
i
<
=
n
do
168
local
w
=
words
[
i
]
169
if
is_upper
(
w
)
then
170
firstnames
[
#
firstnames
+
1
]
,
i
=
w
,
i
+
1
171
else
172
break
173
end
174
end
175
while
i
<
=
n
do
176
local
w
=
words
[
i
]
177
if
is_upper
(
w
)
then
178
break
179
else
180
vons
[
#
vons
+
1
]
,
i
=
w
,
i
+
1
181
end
182
end
183
if
i
<
=
n
then
184
while
i
<
=
n
do
185
surnames
[
#
surnames
+
1
]
,
i
=
words
[
i
]
,
i
+
1
186
end
187
elseif
#
vons
=
=
0
then
188
surnames
[
1
]
=
firstnames
[
#
firstnames
]
189
firstnames
[
#
firstnames
]
=
nil
190
else
191
-- mess
192
end
193
if
#
surnames
=
=
0
then
194
-- safeguard
195
firstnames
=
{
}
196
vons
=
{
}
197
surnames
=
{
author
}
198
else
199
initials
=
makeinitials
(
firstnames
)
200
end
201
elseif
n
=
=
2
then
202
-- {Last, First}
203
-- {von Last, First}
204
local
words
=
lpegmatch
(
spacesplitter
,
split
[
1
]
)
205
local
i
=
1
206
local
n
=
#
words
207
firstnames
=
{
}
208
vons
=
{
}
209
surnames
=
{
}
210
while
i
<
=
n
do
211
local
w
=
words
[
i
]
212
if
is_upper
(
w
)
then
213
break
214
else
215
vons
[
#
vons
+
1
]
,
i
=
w
,
i
+
1
216
end
217
end
218
while
i
<
=
n
do
219
surnames
[
#
surnames
+
1
]
=
words
[
i
]
220
i
=
i
+
1
221
end
222
--
223
local
words
=
lpegmatch
(
spacesplitter
,
split
[
2
]
)
224
local
i
=
1
225
local
n
=
#
words
226
while
i
<
=
n
do
227
local
w
=
words
[
i
]
228
if
is_upper
(
w
)
then
229
firstnames
[
#
firstnames
+
1
]
=
w
230
i
=
i
+
1
231
else
232
break
233
end
234
end
235
while
i
<
=
n
do
236
vons
[
#
vons
+
1
]
=
words
[
i
]
237
i
=
i
+
1
238
end
239
if
surnames
and
firstnames
and
#
surnames
=
=
0
then
240
-- safeguard
241
surnames
[
1
]
=
firstnames
[
#
firstnames
]
242
firstnames
[
#
firstnames
]
=
nil
243
end
244
initials
=
makeinitials
(
firstnames
)
245
elseif
n
=
=
3
then
246
-- {von Last, First, Jr}
247
surnames
=
lpegmatch
(
spacesplitter
,
split
[
1
]
)
248
juniors
=
lpegmatch
(
spacesplitter
,
split
[
2
]
)
249
firstnames
=
lpegmatch
(
spacesplitter
,
split
[
3
]
)
250
initials
=
makeinitials
(
firstnames
)
251
elseif
n
=
=
4
then
252
-- {Von, Last, First, Jr}
253
vons
=
lpegmatch
(
spacesplitter
,
split
[
1
]
)
254
surnames
=
lpegmatch
(
spacesplitter
,
split
[
2
]
)
255
juniors
=
lpegmatch
(
spacesplitter
,
split
[
3
]
)
256
firstnames
=
lpegmatch
(
spacesplitter
,
split
[
4
]
)
257
initials
=
makeinitials
(
firstnames
)
258
elseif
n
>
=
5
then
259
-- {Von, Last, First, Jr, F.}
260
-- {Von, Last, First, Jr, Fr., options}
261
vons
=
lpegmatch
(
spacesplitter
,
split
[
1
]
)
262
surnames
=
lpegmatch
(
spacesplitter
,
split
[
2
]
)
263
juniors
=
lpegmatch
(
spacesplitter
,
split
[
3
]
)
264
firstnames
=
lpegmatch
(
spacesplitter
,
split
[
4
]
)
265
initials
=
lpegmatch
(
spacesplitter
,
split
[
5
]
)
266
options
=
split
[
6
]
267
if
options
then
268
options
=
lpegmatch
(
optionsplitter
,
options
)
269
end
270
end
271
if
firstnames
and
#
firstnames
>
0
then
detail
.
firstnames
=
firstnames
end
272
if
vons
and
#
vons
>
0
then
detail
.
vons
=
vons
end
273
if
surnames
and
#
surnames
>
0
then
detail
.
surnames
=
surnames
end
274
if
initials
and
#
initials
>
0
then
detail
.
initials
=
initials
end
275
if
juniors
and
#
juniors
>
0
then
detail
.
juniors
=
juniors
end
276
if
options
and
next
(
options
)
then
detail
.
options
=
options
end
277
if
not
justsplit
then
278
cache
[
author
]
=
detail
279
nofhits
=
nofhits
+
1
280
end
281
return
detail
282
end
283 284
local
function
splitauthorstring
(
str
)
285
if
not
str
or
str
=
=
"
"
then
286
return
287
end
288
nofused
=
nofused
+
1
289 290
local
remapped
=
authormap
[
str
]
291
if
remapped
then
292
local
detail
=
cache
[
remapped
]
293
if
detail
then
294
cache
[
str
]
=
detail
295
return
{
detail
}
296
end
297
end
298 299
local
authors
=
cache
[
str
]
300
if
authors
then
301
return
{
authors
}
-- we assume one author
302
end
303 304
-- we could cache these too but it can become messy .. leave that for later
305 306
local
authors
=
lpegmatch
(
andsplitter
,
str
)
or
{
}
-- maybe fake an author
307
local
nofauthors
=
#
authors
308
for
i
=
1
,
nofauthors
do
309
authors
[
i
]
=
splitauthor
(
authors
[
i
]
)
310
end
311
if
nofauthors
>
1
and
authors
[
nofauthors
]
.
original
=
=
"
others
"
then
312
-- only the last one is looked at
313
authors
[
nofauthors
]
=
nil
314
authors
.
others
=
true
315
end
316
return
authors
317
end
318 319
publications
.
splitoneauthor
=
splitauthor
320
publications
.
splitauthor
=
splitauthorstring
321 322
local
function
the_initials
(
initials
,
symbol
,
connector
)
323
if
not
symbol
then
324
symbol
=
"
.
"
325
end
326
if
not
connector
then
327
connector
=
"
-
"
328
end
329
local
result
=
{
}
330
local
r
=
0
331
for
i
=
1
,
#
initials
do
332
local
initial
=
initials
[
i
]
333
if
type
(
initial
)
=
=
"
table
"
then
334
-- J.-J.
335
local
set
=
{
}
336
local
s
=
0
337
for
i
=
1
,
#
initial
do
338
if
i
>
1
then
339
s
=
s
+
1
;
set
[
s
]
=
connector
340
end
341
s
=
s
+
1
;
set
[
s
]
=
initial
[
i
]
342
s
=
s
+
1
;
set
[
s
]
=
symbol
343
end
344
r
=
r
+
1
;
result
[
r
]
=
concat
(
set
)
345
else
346
-- J.
347
r
=
r
+
1
;
result
[
r
]
=
initial
.
.
symbol
348
end
349
end
350
return
result
351
end
352 353
local
ctx_btxsetconcat
=
context
.
btxsetconcat
354
local
ctx_btxsetoverflow
=
context
.
btxsetoverflow
355
local
ctx_btxsetinitials
=
context
.
btxsetinitials
356
local
ctx_btxsetfirstnames
=
context
.
btxsetfirstnames
357
local
ctx_btxsetvons
=
context
.
btxsetvons
358
local
ctx_btxsetsurnames
=
context
.
btxsetsurnames
359
local
ctx_btxsetjuniors
=
context
.
btxsetjuniors
360
local
ctx_btxsetauthorvariant
=
context
.
btxsetauthorvariant
361 362
local
ctx_btxstartauthor
=
context
.
btxstartauthor
363
local
ctx_btxstopauthor
=
context
.
btxstopauthor
364 365
local
ctx_btxciteauthorsetup
=
context
.
btxciteauthorsetup
366
local
ctx_btxlistauthorsetup
=
context
.
btxlistauthorsetup
367 368
local
concatstate
=
publications
.
concatstate
369
local
f_invalid
=
formatters
[
"
<invalid %s: %s>
"
]
370 371
local
currentauthordata
=
nil
372
local
currentauthorsymbol
=
nil
373
local
currentauthorconnector
=
nil
374 375
local
manipulators
=
typesetters
.
manipulators
376
local
splitmanipulation
=
manipulators
.
splitspecification
377
local
applymanipulation
=
manipulators
.
applyspecification
378 379
local
function
value
(
i
,
field
)
380
if
currentauthordata
then
381
local
entry
=
currentauthordata
[
i
]
382
if
entry
then
383
local
value
=
entry
[
field
]
384
if
value
and
#
value
>
0
then
385
return
value
386
end
387
end
388
end
389
end
390 391
implement
{
name
=
"
btxcurrentfirstnames
"
,
arguments
=
"
integer
"
,
actions
=
function
(
i
)
local
v
=
value
(
i
,
"
firstnames
"
)
if
v
then
context
(
concat
(
v
,
"
"
)
)
end
end
}
392
implement
{
name
=
"
btxcurrentinitials
"
,
arguments
=
"
integer
"
,
actions
=
function
(
i
)
local
v
=
value
(
i
,
"
initials
"
)
if
v
then
context
(
concat
(
the_initials
(
v
,
currentauthorsymbol
,
currentauthorconnector
)
)
)
end
end
}
393
implement
{
name
=
"
btxcurrentjuniors
"
,
arguments
=
"
integer
"
,
actions
=
function
(
i
)
local
v
=
value
(
i
,
"
juniors
"
)
if
v
then
context
(
concat
(
v
,
"
"
)
)
end
end
}
394
implement
{
name
=
"
btxcurrentsurnames
"
,
arguments
=
"
integer
"
,
actions
=
function
(
i
)
local
v
=
value
(
i
,
"
surnames
"
)
if
v
then
context
(
concat
(
v
,
"
"
)
)
end
end
}
395
implement
{
name
=
"
btxcurrentvons
"
,
arguments
=
"
integer
"
,
actions
=
function
(
i
)
local
v
=
value
(
i
,
"
vons
"
)
if
v
then
context
(
concat
(
v
,
"
"
)
)
end
end
}
396 397
local
function
btxauthorfield
(
i
,
field
)
398
if
currentauthordata
then
399
local
entry
=
currentauthordata
[
i
]
400
if
entry
then
401
local
manipulator
,
field
=
splitmanipulation
(
field
)
402
local
value
=
entry
[
field
]
403
if
not
value
or
#
value
=
=
0
then
404
-- value, no need for message
405
elseif
manipulator
then
406
for
i
=
1
,
#
value
do
407
if
i
>
1
then
408
context
(
"
"
)
409
end
410
context
(
applymanipulation
(
manipulator
,
value
)
or
value
)
411
end
412
elseif
field
=
=
"
initials
"
then
413
context
(
concat
(
the_initials
(
value
,
currentauthorsymbol
,
currentauthorconnector
)
)
)
414
else
415
context
(
concat
(
value
,
"
"
)
)
416
end
417
end
418
end
419
end
420 421
-- This is somewhat tricky: an author is not always an author but
422
-- can also be a title or key, depending on the (optional) set it's
423
-- in. Also, authors can be combined with years and so and they
424
-- might be called upon mixed with other calls.
425 426
local
function
btxauthor
(
dataset
,
tag
,
field
,
settings
)
427
local
split
,
usedfield
,
kind
=
getcasted
(
dataset
,
tag
,
field
)
428
if
kind
=
=
"
author
"
then
429
local
max
=
split
and
#
split
or
0
430
if
max
=
=
0
then
431
return
432
-- error
433
end
434
local
absmax
=
max
435
local
etallimit
=
tonumber
(
settings
.
etallimit
)
or
1000
436
local
etaldisplay
=
tonumber
(
settings
.
etaldisplay
)
or
etallimit
437
local
etaloption
=
settings_to_hash
(
settings
.
etaloption
or
"
"
)
438
local
etallast
=
etaloption
[
v_last
]
439
local
combiner
=
settings
.
combiner
440
local
symbol
=
settings
.
symbol
441
local
connector
=
settings
.
connector
442
local
index
=
settings
.
index
443
if
not
combiner
or
combiner
=
=
"
"
then
444
combiner
=
"
normal
"
445
end
446
if
not
symbol
then
447
symbol
=
"
.
"
448
end
449
local
ctx_btxsetup
=
settings
.
kind
=
=
"
cite
"
and
ctx_btxciteauthorsetup
or
ctx_btxlistauthorsetup
450
if
max
>
etallimit
and
(
etaldisplay
+
(
etallast
and
1
or
0
)
)
<
max
then
451
max
=
etaldisplay
452
else
453
etallast
=
false
454
end
455
currentauthordata
=
split
456
currentauthorsymbol
=
symbol
457
currentauthorconnector
=
connector
458 459
local
function
oneauthor
(
i
,
last
,
justone
)
460
local
author
=
split
[
i
]
461
if
index
then
462
ctx_btxstartauthor
(
i
,
1
,
0
)
463
elseif
last
then
464
ctx_btxstartauthor
(
i
,
1
,
0
)
465
ctx_btxsetconcat
(
0
)
466
ctx_btxsetauthorvariant
(
combiner
)
467
else
468
local
state
=
author
.
state
or
0
469
ctx_btxstartauthor
(
i
,
max
,
state
)
470
ctx_btxsetconcat
(
concatstate
(
i
,
max
)
)
471
ctx_btxsetauthorvariant
(
combiner
)
472
end
473
local
initials
=
author
.
initials
474
if
initials
and
#
initials
>
0
then
475
ctx_btxsetinitials
(
)
-- (concat(the_initials(initials,symbol)," "))
476
end
477
local
firstnames
=
author
.
firstnames
478
if
firstnames
and
#
firstnames
>
0
then
479
ctx_btxsetfirstnames
(
)
-- (concat(firstnames," "))
480
end
481
local
vons
=
author
.
vons
482
if
vons
and
#
vons
>
0
then
483
ctx_btxsetvons
(
)
-- (concat(vons," "))
484
end
485
local
surnames
=
author
.
surnames
486
if
surnames
and
#
surnames
>
0
then
487
ctx_btxsetsurnames
(
)
-- (concat(surnames," "))
488
end
489
local
juniors
=
author
.
juniors
490
if
juniors
and
#
juniors
>
0
then
491
ctx_btxsetjuniors
(
)
-- (concat(juniors," "))
492
end
493
if
not
index
and
i
=
=
max
then
494
if
split
.
others
then
495
ctx_btxsetoverflow
(
1
)
496
else
497
local
overflow
=
#
split
-
max
498
if
overflow
>
0
then
499
ctx_btxsetoverflow
(
overflow
)
500
end
501
end
502
end
503
ctx_btxsetup
(
combiner
)
504
ctx_btxstopauthor
(
)
505
end
506 507
if
index
then
508
oneauthor
(
index
)
509
elseif
max
=
=
1
then
510
oneauthor
(
1
,
false
,
true
)
511
else
512
for
i
=
1
,
max
do
513
oneauthor
(
i
)
514
end
515
if
etallast
then
516
oneauthor
(
absmax
,
true
)
517
end
518
end
519 520
else
521
report
(
"
ignored field %a of tag %a, used field %a is no author
"
,
field
,
tag
,
usedfield
)
522
end
523 524
end
525 526
implement
{
527
name
=
"
btxauthorfield
"
,
528
actions
=
btxauthorfield
,
529
arguments
=
{
"
integer
"
,
"
string
"
}
530
}
531 532
implement
{
533
name
=
"
btxauthor
"
,
534
actions
=
btxauthor
,
535
arguments
=
{
536
"
argument
"
,
537
"
argument
"
,
538
"
argument
"
,
539
{
540
{
"
combiner
"
}
,
541
{
"
kind
"
}
,
542
{
"
etallimit
"
}
,
543
{
"
etaldisplay
"
}
,
544
{
"
etaloption
"
}
,
545
{
"
symbol
"
}
,
546
{
"
connector
"
}
,
547
}
548
}
549
}
550 551
local
function
components
(
snippet
,
short
)
552
local
vons
=
snippet
.
vons
553
local
surnames
=
snippet
.
surnames
554
local
initials
=
snippet
.
initials
555
local
firstnames
=
not
short
and
snippet
.
firstnames
556
local
juniors
=
snippet
.
juniors
557
return
558
vons
and
#
vons
>
0
and
concat
(
vons
,
"
"
)
or
"
"
,
559
surnames
and
#
surnames
>
0
and
concat
(
surnames
,
"
"
)
or
"
"
,
560
initials
and
#
initials
>
0
and
concat
(
the_initials
(
initials
)
,
"
"
)
or
"
"
,
561
firstnames
and
#
firstnames
>
0
and
concat
(
firstnames
,
"
"
)
or
"
"
,
562
juniors
and
#
juniors
>
0
and
concat
(
juniors
,
"
"
)
or
"
"
563
end
564 565
local
collapsers
=
allocate
{
}
566 567
publications
.
authorcollapsers
=
collapsers
568 569
-- making a constructor doesn't make the code nicer as the_initials is an
570
-- exception
571 572
local
function
default
(
author
)
-- one author
573
local
hash
=
author
.
hash
574
if
hash
then
575
return
hash
576
end
577
local
original
=
author
.
original
578
local
vons
=
author
.
vons
579
local
surnames
=
author
.
surnames
580
local
initials
=
author
.
initials
581
local
firstnames
=
author
.
firstnames
582
local
juniors
=
author
.
juniors
583
local
result
=
{
}
584
local
nofresult
=
0
585
if
vons
and
#
vons
>
0
then
586
for
j
=
1
,
#
vons
do
587
nofresult
=
nofresult
+
1
588
result
[
nofresult
]
=
vons
[
j
]
589
end
590
end
591
if
surnames
and
#
surnames
>
0
then
592
for
j
=
1
,
#
surnames
do
593
nofresult
=
nofresult
+
1
594
result
[
nofresult
]
=
surnames
[
j
]
595
end
596
end
597
if
initials
and
#
initials
>
0
then
598
initials
=
the_initials
(
initials
)
599
for
j
=
1
,
#
initials
do
600
nofresult
=
nofresult
+
1
601
result
[
nofresult
]
=
initials
[
j
]
602
end
603
end
604
if
firstnames
and
#
firstnames
>
0
then
605
for
j
=
1
,
#
firstnames
do
606
nofresult
=
nofresult
+
1
607
result
[
nofresult
]
=
firstnames
[
j
]
608
end
609
end
610
if
juniors
and
#
juniors
>
0
then
611
for
j
=
1
,
#
juniors
do
612
nofresult
=
nofresult
+
1
613
result
[
nofresult
]
=
juniors
[
j
]
614
end
615
end
616
local
hash
=
concat
(
result
,
"
"
)
617
if
trace_hashing
then
618
report
(
"
hash: %s -> %s
"
,
original
,
hash
)
619
end
620
author
.
hash
=
hash
621
return
hash
622
end
623 624
local
authorhashers
=
{
}
625
publications
.
authorhashers
=
authorhashers
626 627
-- todo: some hashing
628 629
local
function
name
(
authors
)
630
if
type
(
authors
)
=
=
"
table
"
then
631
local
n
=
#
authors
632
if
n
=
=
0
then
633
return
"
"
634
end
635
local
result
=
{
}
636
local
nofresult
=
0
637
for
i
=
1
,
n
do
638
local
author
=
authors
[
i
]
639
local
surnames
=
author
.
surnames
640
if
surnames
and
#
surnames
>
0
then
641
for
j
=
1
,
#
surnames
do
642
nofresult
=
nofresult
+
1
643
result
[
nofresult
]
=
surnames
[
j
]
644
end
645
end
646
end
647
return
concat
(
result
,
"
"
)
648
else
649
return
authors
650
end
651
end
652 653
table
.
setmetatableindex
(
authorhashers
,
function
(
t
,
k
)
654
t
[
k
]
=
name
655
return
name
656
end
)
657 658
authorhashers
.
normal
=
function
(
authors
)
659
if
type
(
authors
)
=
=
"
table
"
then
660
local
n
=
#
authors
661
if
n
=
=
0
then
662
return
"
"
663
end
664
local
result
=
{
}
665
local
nofresult
=
0
666
for
i
=
1
,
n
do
667
local
author
=
authors
[
i
]
668
local
vons
=
author
.
vons
669
local
surnames
=
author
.
surnames
670
local
firstnames
=
author
.
firstnames
671
local
juniors
=
author
.
juniors
672
if
vons
and
#
vons
>
0
then
673
for
j
=
1
,
#
vons
do
674
nofresult
=
nofresult
+
1
675
result
[
nofresult
]
=
vons
[
j
]
676
end
677
end
678
if
surnames
and
#
surnames
>
0
then
679
for
j
=
1
,
#
surnames
do
680
nofresult
=
nofresult
+
1
681
result
[
nofresult
]
=
surnames
[
j
]
682
end
683
end
684
if
firstnames
and
#
firstnames
>
0
then
685
for
j
=
1
,
#
firstnames
do
686
nofresult
=
nofresult
+
1
687
result
[
nofresult
]
=
firstnames
[
j
]
688
end
689
end
690
if
juniors
and
#
juniors
>
0
then
691
for
j
=
1
,
#
juniors
do
692
nofresult
=
nofresult
+
1
693
result
[
nofresult
]
=
juniors
[
j
]
694
end
695
end
696
end
697
return
concat
(
result
,
"
"
)
698
else
699
return
authors
700
end
701
end
702 703
authorhashers
.
normalshort
=
function
(
authors
)
704
if
type
(
authors
)
=
=
"
table
"
then
705
local
n
=
#
authors
706
if
n
=
=
0
then
707
return
"
"
708
end
709
local
result
=
{
}
710
local
nofresult
=
0
711
for
i
=
1
,
n
do
712
local
author
=
authors
[
i
]
713
local
vons
=
author
.
vons
714
local
surnames
=
author
.
surnames
715
local
initials
=
author
.
initials
716
local
juniors
=
author
.
juniors
717
if
vons
and
#
vons
>
0
then
718
for
j
=
1
,
#
vons
do
719
nofresult
=
nofresult
+
1
720
result
[
nofresult
]
=
vons
[
j
]
721
end
722
end
723
if
surnames
and
#
surnames
>
0
then
724
for
j
=
1
,
#
surnames
do
725
nofresult
=
nofresult
+
1
726
result
[
nofresult
]
=
surnames
[
j
]
727
end
728
end
729
if
initials
and
#
initials
>
0
then
730
initials
=
the_initials
(
initials
)
731
for
j
=
1
,
#
initials
do
732
nofresult
=
nofresult
+
1
733
result
[
nofresult
]
=
initials
[
j
]
734
end
735
end
736
if
juniors
and
#
juniors
>
0
then
737
for
j
=
1
,
#
juniors
do
738
nofresult
=
nofresult
+
1
739
result
[
nofresult
]
=
juniors
[
j
]
740
end
741
end
742
end
743
return
concat
(
result
,
"
"
)
744
else
745
return
authors
746
end
747
end
748 749
local
sequentialhash
=
function
(
authors
)
750
if
type
(
authors
)
=
=
"
table
"
then
751
local
n
=
#
authors
752
if
n
=
=
0
then
753
return
"
"
754
end
755
local
result
=
{
}
756
local
nofresult
=
0
757
for
i
=
1
,
n
do
758
local
author
=
authors
[
i
]
759
local
vons
=
author
.
vons
760
local
surnames
=
author
.
surnames
761
local
firstnames
=
author
.
firstnames
762
local
juniors
=
author
.
juniors
763
if
firstnames
and
#
firstnames
>
0
then
764
for
j
=
1
,
#
firstnames
do
765
nofresult
=
nofresult
+
1
766
result
[
nofresult
]
=
firstnames
[
j
]
767
end
768
end
769
if
vons
and
#
vons
>
0
then
770
for
j
=
1
,
#
vons
do
771
nofresult
=
nofresult
+
1
772
result
[
nofresult
]
=
vons
[
j
]
773
end
774
end
775
if
surnames
and
#
surnames
>
0
then
776
for
j
=
1
,
#
surnames
do
777
nofresult
=
nofresult
+
1
778
result
[
nofresult
]
=
surnames
[
j
]
779
end
780
end
781
if
juniors
and
#
juniors
>
0
then
782
for
j
=
1
,
#
juniors
do
783
nofresult
=
nofresult
+
1
784
result
[
nofresult
]
=
juniors
[
j
]
785
end
786
end
787
end
788
return
concat
(
result
,
"
"
)
789
else
790
return
authors
791
end
792
end
793 794
local
sequentialshorthash
=
function
(
authors
)
795
if
type
(
authors
)
=
=
"
table
"
then
796
local
n
=
#
authors
797
if
n
=
=
0
then
798
return
"
"
799
end
800
local
result
=
{
}
801
local
nofresult
=
0
802
for
i
=
1
,
n
do
803
local
author
=
authors
[
i
]
804
local
vons
=
author
.
vons
805
local
surnames
=
author
.
surnames
806
local
initials
=
author
.
initials
807
local
juniors
=
author
.
juniors
808
if
initials
and
#
initials
>
0
then
809
initials
=
the_initials
(
initials
)
810
for
j
=
1
,
#
initials
do
811
nofresult
=
nofresult
+
1
812
result
[
nofresult
]
=
initials
[
j
]
813
end
814
end
815
if
vons
and
#
vons
>
0
then
816
for
j
=
1
,
#
vons
do
817
nofresult
=
nofresult
+
1
818
result
[
nofresult
]
=
vons
[
j
]
819
end
820
end
821
if
surnames
and
#
surnames
>
0
then
822
for
j
=
1
,
#
surnames
do
823
nofresult
=
nofresult
+
1
824
result
[
nofresult
]
=
surnames
[
j
]
825
end
826
end
827
if
juniors
and
#
juniors
>
0
then
828
for
j
=
1
,
#
juniors
do
829
nofresult
=
nofresult
+
1
830
result
[
nofresult
]
=
juniors
[
j
]
831
end
832
end
833
end
834
return
concat
(
result
,
"
"
)
835
else
836
return
authors
837
end
838
end
839 840
authorhashers
.
sequential
=
sequentialhash
841
authorhashers
.
sequentialshort
=
sequentialshorthash
842
authorhashers
.
normalinverted
=
authorhashers
.
normal
843
authorhashers
.
invertedshort
=
authorhashers
.
normalshort
844 845
local
p_clean
=
Cs
(
(
846
P
(
"
\\btxcmd
"
)
/
"
"
-- better keep the argument
847
+
S
(
"
`~!@#$%^&*()_-+={}[]:;\"\'<>,.?/|\\
"
)
/
"
"
848
+
lpeg
.
patterns
.
utf8character
849
)
^
1
)
850 851
-- Probabbly more robust is a two pass approach.
852 853
authorhashers
.
short
=
function
(
authors
)
854
-- a short is a real dumb hardcoded kind of tag and we only support
855
-- this one because some users might expect it, not because it makes
856
-- sense
857
if
type
(
authors
)
=
=
"
table
"
then
858
local
n
=
#
authors
859
if
n
=
=
0
then
860
return
"
unk
"
861
elseif
n
=
=
1
then
862
local
surnames
=
authors
[
1
]
.
surnames
863
if
not
surnames
or
#
surnames
=
=
0
then
864
return
"
err
"
865
else
866
local
s
=
surnames
[
1
]
867
local
c
=
lpegmatch
(
p_clean
,
s
)
868
if
trace_hashing
and
s
~
=
c
then
869
report_cite
(
"
name %a cleaned to %a for short construction
"
,
s
,
c
)
870
end
871
return
utfsub
(
c
,
1
,
3
)
872
end
873
else
874
local
t
=
{
}
875
for
i
=
1
,
n
do
876
if
i
>
3
then
877
t
[
#
t
+
1
]
=
"
+
"
-- indeed
878
break
879
end
880
local
surnames
=
authors
[
i
]
.
surnames
881
if
not
surnames
or
#
surnames
=
=
0
then
882
t
[
#
t
+
1
]
=
"
?
"
883
else
884
local
s
=
surnames
[
1
]
885
local
c
=
lpegmatch
(
p_clean
,
s
)
886
if
s
~
=
c
then
887
report_cite
(
"
name %a cleaned to %a for short construction
"
,
s
,
c
)
888
end
889
t
[
#
t
+
1
]
=
utfsub
(
c
,
1
,
1
)
890
end
891
end
892
return
concat
(
t
)
893
end
894
else
895
return
utfsub
(
authors
,
1
,
3
)
896
end
897
end
898 899
collapsers
.
default
=
default
900 901
local
function
authorwriter
(
key
,
index
)
902
if
not
key
then
903
return
"
"
904
end
905
if
type
(
key
)
=
=
"
string
"
then
906
return
key
907
end
908
local
n
=
#
key
909
if
n
=
=
0
then
910
return
"
"
911
end
912
if
index
then
913
if
not
key
[
index
]
then
914
return
"
"
915
end
916
elseif
n
=
=
1
then
917
index
=
1
918
end
919
if
index
then
920
local
author
=
key
[
index
]
921
local
options
=
author
.
options
922
if
options
then
923
for
option
in
next
,
options
do
924
local
collapse
=
collapsers
[
option
]
925
if
collapse
then
926
return
collapse
(
author
)
927
end
928
end
929
end
930
local
hash
=
default
(
author
)
931
-- if trace_hashing then
932
-- report("hash: %s",hash)
933
-- end
934
return
hash
935
end
936
local
t
=
{
}
937
local
s
=
0
938
for
i
=
1
,
n
do
939
local
author
=
key
[
i
]
940
local
options
=
author
.
options
941
s
=
s
+
1
942
if
options
then
943
local
done
=
false
944
for
option
in
next
,
options
do
945
local
collapse
=
collapsers
[
option
]
946
if
collapse
then
947
t
[
s
]
=
collapse
(
author
)
948
done
=
true
949
end
950
end
951
if
not
done
then
952
t
[
s
]
=
default
(
author
)
953
end
954
else
955
t
[
s
]
=
default
(
author
)
956
end
957
end
958
local
hash
=
concat
(
t
,
"
&
"
)
959
-- if trace_hashing then
960
-- report("hash: %s",hash)
961
-- end
962
return
hash
963
end
964 965
local
function
writer
(
key
)
966
return
authorwriter
(
key
)
-- discard extra arguments in the caller
967
end
968 969
publications
.
writers
.
author
=
writer
970
publications
.
casters
.
author
=
splitauthorstring
971
publications
.
components
.
author
=
components
972 973
-- sharedmethods.author = {
974
-- { field = "key", default = "", unknown = "" },
975
-- { field = "author", default = "", unknown = "" },
976
-- { field = "title", default = "", unknown = "" },
977
-- }
978 979
-- Analysis of the APA by Alan:
980
--
981
-- first : key author editor publisher title journal volume number pages
982
-- second: year suffix title month day journal volume number
983 984
publications
.
sortmethods
.
authoryear
=
{
985
sequence
=
{
986
-- { field = "key", default = "ZZZZ", unknown = "ZZZZ" },
987
{
field
=
"
author
"
,
default
=
"
"
,
unknown
=
"
"
}
,
988
{
field
=
"
year
"
,
default
=
"
9998
"
,
unknown
=
"
9999
"
}
,
989
-- { field = "suffix", default = " ", unknown = " " },
990
{
field
=
"
month
"
,
default
=
"
13
"
,
unknown
=
"
14
"
}
,
991
{
field
=
"
day
"
,
default
=
"
32
"
,
unknown
=
"
33
"
}
,
992
{
field
=
"
journal
"
,
default
=
"
"
,
unknown
=
"
"
}
,
993
{
field
=
"
volume
"
,
default
=
"
"
,
unknown
=
"
"
}
,
994
-- { field = "number", default = "", unknown = "" },
995
{
field
=
"
pages
"
,
default
=
"
"
,
unknown
=
"
"
}
,
996
{
field
=
"
title
"
,
default
=
"
"
,
unknown
=
"
"
}
,
997
{
field
=
"
index
"
,
default
=
"
"
,
unknown
=
"
"
}
,
998
}
,
999
}
1000 1001
publications
.
sortmethods
.
authortitle
=
{
1002
sequence
=
{
1003
{
field
=
"
author
"
,
default
=
"
"
,
unknown
=
"
"
}
,
1004
{
field
=
"
title
"
,
default
=
"
"
,
unknown
=
"
"
}
,
1005
{
field
=
"
booktitle
"
,
default
=
"
"
,
unknown
=
"
"
}
,
-- if this is an untitled section (e.g., introduction, foreword, preface) of a book or a review of a book
1006
{
field
=
"
maintitle
"
,
default
=
"
"
,
unknown
=
"
"
}
,
-- if this is an untitled section or volume in a multivolume collection
1007
{
field
=
"
volume
"
,
default
=
"
"
,
unknown
=
"
"
}
,
1008
{
field
=
"
part
"
,
default
=
"
"
,
unknown
=
"
"
}
,
1009
{
field
=
"
date
"
,
default
=
"
9998-13-32
"
,
unknown
=
"
9999-14-33
"
}
,
-- some specifications allow date instead of year, month, day
1010
{
field
=
"
year
"
,
default
=
"
9998
"
,
unknown
=
"
9999
"
}
,
1011
{
field
=
"
month
"
,
default
=
"
13
"
,
unknown
=
"
14
"
}
,
1012
{
field
=
"
day
"
,
default
=
"
32
"
,
unknown
=
"
33
"
}
,
1013
{
field
=
"
index
"
,
default
=
"
"
,
unknown
=
"
"
}
,
1014
}
,
1015
}
1016 1017
implement
{
1018
name
=
"
btxremapauthor
"
,
1019
arguments
=
"
2 strings
"
,
1020
actions
=
function
(
k
,
v
)
1021
local
a
=
{
splitauthor
(
k
,
true
)
}
1022
local
s1
=
sequentialhash
(
a
)
1023
local
s2
=
sequentialshorthash
(
a
)
1024
if
not
authormap
[
k
]
then
1025
authormap
[
k
]
=
v
1026
report
(
"
%a mapped onto %a
"
,
k
,
v
)
1027
end
1028
if
not
authormap
[
s1
]
then
1029
authormap
[
s1
]
=
v
1030
report
(
"
%a mapped onto %a, derived from %a
"
,
s1
,
v
,
k
)
1031
end
1032
if
not
authormap
[
s2
]
then
1033
authormap
[
s2
]
=
v
1034
report
(
"
%a mapped onto %a, derived from %a
"
,
s2
,
v
,
k
)
1035
end
1036
end
1037
}
1038 1039
implement
{
1040
name
=
"
btxshowauthorremapping
"
,
1041
actions
=
function
(
k
,
v
)
1042
report
(
"
start author remapping
"
)
1043
for
k
,
v
in
sortedhash
(
authormap
)
do
1044
report
(
"
%s => %s
"
,
k
,
v
)
1045
end
1046
report
(
"
stop author remapping
"
)
1047
end
1048
}
1049