Skip to content

Commit 646e3a4

Browse files
Omikhleiaalerque
authored andcommitted
feat(packages): Biblatex data inheritance and field mapping
Add (a part of) the BibLaTeX data inheritance rules for cross-references. Use BibLaTeX field names, but still support the legacy BibTeX file names. As part of these refactors, allow loading more than one bibliography file.
1 parent 63083ad commit 646e3a4

File tree

3 files changed

+125
-15
lines changed

3 files changed

+125
-15
lines changed

packages/bibtex/bibmaps.lua

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
-- Mappings for aliases and inheritance rules
2+
3+
-- Partial implementation of the Biber/BibLaTeX data inheritance rules
4+
-- (derived from the biblatex package manual v3.20, appendix A)
5+
-- FIXME: This is not complete
6+
local crossrefmap = {
7+
book = {
8+
inbook = {
9+
author = "author", -- inbook inherits author from book author
10+
bookauthor = "author", -- inbook inherits bookauthor from book author
11+
indexsorttitle = false, -- inbook skips (=does not inherit) indexsorttitle from book
12+
indextitle = false,
13+
shorttitle = false,
14+
sorttitle = false,
15+
subtitle = "booksubtitle",
16+
title = "booktitle",
17+
titleaddon = "booktitleaddon",
18+
},
19+
},
20+
periodical = {
21+
article = {
22+
indexsorttitle = false,
23+
indextitle = false,
24+
shorttitle = false,
25+
sorttitle = false,
26+
subtitle = "journalsubtitle",
27+
title = "journaltitle",
28+
titleaddon = "journaltitleaddon",
29+
},
30+
},
31+
proceedings = {
32+
inproceedings = {
33+
indexsorttitle = false,
34+
indextitle = false,
35+
shorttitle = false,
36+
sorttitle = false,
37+
subtitle = "booksubtitle",
38+
title = "booktitle",
39+
titleaddon = "booktitleaddon",
40+
},
41+
},
42+
}
43+
44+
-- biblatex field aliases
45+
-- From biblatex package manual v3.20, section 2.2.5
46+
local fieldmap = {
47+
address = "location",
48+
annote = "annotation",
49+
archiveprefix = "eprinttype",
50+
key = "sortkey",
51+
pdf = "file",
52+
journal = "journaltitle",
53+
primaryclass = "eprintclass",
54+
school = "institution",
55+
}
56+
57+
return {
58+
crossrefmap = crossrefmap,
59+
fieldmap = fieldmap,
60+
}

packages/bibtex/init.lua

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,30 @@ end)
4747
-- stylua: ignore end
4848
---@diagnostic enable: undefined-global, unused-local, lowercase-global
4949

50-
local parseBibtex = function (fn)
50+
local bibcompat = require("packages.bibtex.bibmaps")
51+
local crossrefmap, fieldmap = bibcompat.crossrefmap, bibcompat.fieldmap
52+
53+
local function consolidateEntry (entry, label)
54+
local consolidated = {}
55+
for field, value in pairs(entry.attributes) do
56+
consolidated[field] = value
57+
local alias = fieldmap[field]
58+
if alias then
59+
if entry.attributes[alias] then
60+
SU.warn("Duplicate field '" .. field .. "' and alias '" .. alias .. "' in entry '" .. label .. "'")
61+
else
62+
consolidated[alias] = value
63+
end
64+
end
65+
end
66+
entry.attributes = consolidated
67+
return entry
68+
end
69+
70+
--- Parse a BibTeX file and populate a bibliography table.
71+
-- @tparam string fn Filename
72+
-- @tparam table biblio Table of entries
73+
local function parseBibtex (fn, biblio)
5174
fn = SILE.resolveFile(fn) or SU.error("Unable to resolve Bibtex file " .. fn)
5275
local fh, e = io.open(fn)
5376
if e then
@@ -58,14 +81,45 @@ local parseBibtex = function (fn)
5881
if not t or not t[1] or t.id ~= "document" then
5982
SU.error("Error parsing bibtex")
6083
end
61-
local entries = {}
6284
for i = 1, #t do
6385
if t[i].id == "entry" then
6486
local ent = t[i][1]
65-
entries[ent.label] = { type = ent.type, attributes = ent[1] }
87+
local entry = { type = ent.type, attributes = ent[1] }
88+
if biblio[ent.label] then
89+
SU.warn("Duplicate entry key '" .. ent.label .. "', picking the last one")
90+
end
91+
biblio[ent.label] = consolidateEntry(entry, ent.label)
92+
end
93+
end
94+
end
95+
96+
--- Copy fields from the parent entry to the child entry.
97+
-- BibLaTeX/Biber have a complex inheritance system for fields.
98+
-- This implementation is more naive, but should be sufficient for reasonable
99+
-- use cases.
100+
-- @tparam table parent Parent entry
101+
-- @tparam table entry Child entry
102+
local function fieldsInherit (parent, entry)
103+
local map = crossrefmap[parent.type] and crossrefmap[parent.type][entry.type]
104+
if not map then
105+
-- @xdata and any other unknown types: inherit all missing fields
106+
for field, value in pairs(parent.attributes) do
107+
if not entry.attributes[field] then
108+
entry.attributes[field] = value
109+
end
110+
end
111+
return -- done
112+
end
113+
for field, value in pairs(parent.attributes) do
114+
if map[field] == nil and not entry.attributes[field] then
115+
entry.attributes[field] = value
116+
end
117+
for childfield, parentfield in pairs(map) do
118+
if parentfield and not entry.attributes[parentfield] then
119+
entry.attributes[parentfield] = parent.attributes[childfield]
120+
end
66121
end
67122
end
68-
return entries
69123
end
70124

71125
--- Resolve the 'crossref' and 'xdata' fields on a bibliography entry.
@@ -104,11 +158,7 @@ local function crossrefAndXDataResolve (bib, entry)
104158
local parent = bib[ref]
105159
if parent then
106160
crossrefAndXDataResolve(bib, parent)
107-
for k, v in pairs(parent.attributes) do
108-
if not entry.attributes[k] then
109-
entry.attributes[k] = v
110-
end
111-
end
161+
fieldsInherit(parent, entry)
112162
else
113163
SU.warn("Unknown crossref " .. ref .. " in bibliography entry " .. entry.label)
114164
end
@@ -133,7 +183,7 @@ end
133183
function package:registerCommands ()
134184
self:registerCommand("loadbibliography", function (options, _)
135185
local file = SU.required(options, "file", "loadbibliography")
136-
SILE.scratch.bibtex.bib = parseBibtex(file) -- Later we'll do multiple bibliogs, but not now
186+
parseBibtex(file, SILE.scratch.bibtex.bib)
137187
end)
138188

139189
self:registerCommand("bibstyle", function (_, _)

packages/bibtex/styles/chicago.lua

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ local ChicagoStyles = pl.tablex.merge(Bibliography.Style, {
2020
". ",
2121
quotes(title, "."),
2222
" ",
23-
italic(journal),
23+
italic(journaltitle),
2424
optional(" ", volume),
2525
optional(" no. ", number),
2626
optional(" ", parens(optional(month, " "), year)),
@@ -34,7 +34,7 @@ local ChicagoStyles = pl.tablex.merge(Bibliography.Style, {
3434
". ",
3535
quotes(title, "."),
3636
" ",
37-
italic(journal),
37+
italic(journaltitle),
3838
optional(", ", month),
3939
optional(", ", year),
4040
optional(": ", pageRange),
@@ -59,7 +59,7 @@ local ChicagoStyles = pl.tablex.merge(Bibliography.Style, {
5959
" ",
6060
optional("In ", italic(booktitle), ". "),
6161
optional(transEditor, ". "),
62-
optional(address, ": "),
62+
optional(location, ": "),
6363
optional(pub, year and ", " or ". "),
6464
optional(year, ". "),
6565
optional(number, ". "),
@@ -70,7 +70,7 @@ local ChicagoStyles = pl.tablex.merge(Bibliography.Style, {
7070
italic(title),
7171
". ",
7272
optional(transEditor, ". "),
73-
optional(address, ": "),
73+
optional(location, ": "),
7474
optional(pub, year and ", " or ". "),
7575
optional(year, ". "),
7676
optional(number, ". "),
@@ -85,7 +85,7 @@ local ChicagoStyles = pl.tablex.merge(Bibliography.Style, {
8585
" ",
8686
optional(transEditor, ". "),
8787
optional(bibtype, ". "), -- "type" from BibTeX entry
88-
optional(address, ": "),
88+
optional(location, ": "),
8989
optional(pub, ", "),
9090
optional(year, ".")
9191
end,

0 commit comments

Comments
 (0)