Skip to content

Commit

Permalink
feat(packages): Biblatex data inheritance and field mapping
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Omikhleia authored and alerque committed Jun 23, 2024
1 parent 63083ad commit 646e3a4
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 15 deletions.
60 changes: 60 additions & 0 deletions packages/bibtex/bibmaps.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
-- Mappings for aliases and inheritance rules

-- Partial implementation of the Biber/BibLaTeX data inheritance rules
-- (derived from the biblatex package manual v3.20, appendix A)
-- FIXME: This is not complete
local crossrefmap = {
book = {
inbook = {
author = "author", -- inbook inherits author from book author
bookauthor = "author", -- inbook inherits bookauthor from book author
indexsorttitle = false, -- inbook skips (=does not inherit) indexsorttitle from book
indextitle = false,
shorttitle = false,
sorttitle = false,
subtitle = "booksubtitle",
title = "booktitle",
titleaddon = "booktitleaddon",
},
},
periodical = {
article = {
indexsorttitle = false,
indextitle = false,
shorttitle = false,
sorttitle = false,
subtitle = "journalsubtitle",
title = "journaltitle",
titleaddon = "journaltitleaddon",
},
},
proceedings = {
inproceedings = {
indexsorttitle = false,
indextitle = false,
shorttitle = false,
sorttitle = false,
subtitle = "booksubtitle",
title = "booktitle",
titleaddon = "booktitleaddon",
},
},
}

-- biblatex field aliases
-- From biblatex package manual v3.20, section 2.2.5
local fieldmap = {
address = "location",
annote = "annotation",
archiveprefix = "eprinttype",
key = "sortkey",
pdf = "file",
journal = "journaltitle",
primaryclass = "eprintclass",
school = "institution",
}

return {
crossrefmap = crossrefmap,
fieldmap = fieldmap,
}
70 changes: 60 additions & 10 deletions packages/bibtex/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,30 @@ end)
-- stylua: ignore end
---@diagnostic enable: undefined-global, unused-local, lowercase-global

local parseBibtex = function (fn)
local bibcompat = require("packages.bibtex.bibmaps")
local crossrefmap, fieldmap = bibcompat.crossrefmap, bibcompat.fieldmap

local function consolidateEntry (entry, label)
local consolidated = {}
for field, value in pairs(entry.attributes) do
consolidated[field] = value
local alias = fieldmap[field]
if alias then
if entry.attributes[alias] then
SU.warn("Duplicate field '" .. field .. "' and alias '" .. alias .. "' in entry '" .. label .. "'")
else
consolidated[alias] = value
end
end
end
entry.attributes = consolidated
return entry
end

--- Parse a BibTeX file and populate a bibliography table.
-- @tparam string fn Filename
-- @tparam table biblio Table of entries
local function parseBibtex (fn, biblio)
fn = SILE.resolveFile(fn) or SU.error("Unable to resolve Bibtex file " .. fn)
local fh, e = io.open(fn)
if e then
Expand All @@ -58,14 +81,45 @@ local parseBibtex = function (fn)
if not t or not t[1] or t.id ~= "document" then
SU.error("Error parsing bibtex")
end
local entries = {}
for i = 1, #t do
if t[i].id == "entry" then
local ent = t[i][1]
entries[ent.label] = { type = ent.type, attributes = ent[1] }
local entry = { type = ent.type, attributes = ent[1] }
if biblio[ent.label] then
SU.warn("Duplicate entry key '" .. ent.label .. "', picking the last one")
end
biblio[ent.label] = consolidateEntry(entry, ent.label)
end
end
end

--- Copy fields from the parent entry to the child entry.
-- BibLaTeX/Biber have a complex inheritance system for fields.
-- This implementation is more naive, but should be sufficient for reasonable
-- use cases.
-- @tparam table parent Parent entry
-- @tparam table entry Child entry
local function fieldsInherit (parent, entry)
local map = crossrefmap[parent.type] and crossrefmap[parent.type][entry.type]
if not map then
-- @xdata and any other unknown types: inherit all missing fields
for field, value in pairs(parent.attributes) do
if not entry.attributes[field] then
entry.attributes[field] = value
end
end
return -- done
end
for field, value in pairs(parent.attributes) do
if map[field] == nil and not entry.attributes[field] then
entry.attributes[field] = value
end
for childfield, parentfield in pairs(map) do
if parentfield and not entry.attributes[parentfield] then
entry.attributes[parentfield] = parent.attributes[childfield]
end
end
end
return entries
end

--- Resolve the 'crossref' and 'xdata' fields on a bibliography entry.
Expand Down Expand Up @@ -104,11 +158,7 @@ local function crossrefAndXDataResolve (bib, entry)
local parent = bib[ref]
if parent then
crossrefAndXDataResolve(bib, parent)
for k, v in pairs(parent.attributes) do
if not entry.attributes[k] then
entry.attributes[k] = v
end
end
fieldsInherit(parent, entry)
else
SU.warn("Unknown crossref " .. ref .. " in bibliography entry " .. entry.label)
end
Expand All @@ -133,7 +183,7 @@ end
function package:registerCommands ()
self:registerCommand("loadbibliography", function (options, _)
local file = SU.required(options, "file", "loadbibliography")
SILE.scratch.bibtex.bib = parseBibtex(file) -- Later we'll do multiple bibliogs, but not now
parseBibtex(file, SILE.scratch.bibtex.bib)
end)

self:registerCommand("bibstyle", function (_, _)
Expand Down
10 changes: 5 additions & 5 deletions packages/bibtex/styles/chicago.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ local ChicagoStyles = pl.tablex.merge(Bibliography.Style, {
". ",
quotes(title, "."),
" ",
italic(journal),
italic(journaltitle),
optional(" ", volume),
optional(" no. ", number),
optional(" ", parens(optional(month, " "), year)),
Expand All @@ -34,7 +34,7 @@ local ChicagoStyles = pl.tablex.merge(Bibliography.Style, {
". ",
quotes(title, "."),
" ",
italic(journal),
italic(journaltitle),
optional(", ", month),
optional(", ", year),
optional(": ", pageRange),
Expand All @@ -59,7 +59,7 @@ local ChicagoStyles = pl.tablex.merge(Bibliography.Style, {
" ",
optional("In ", italic(booktitle), ". "),
optional(transEditor, ". "),
optional(address, ": "),
optional(location, ": "),
optional(pub, year and ", " or ". "),
optional(year, ". "),
optional(number, ". "),
Expand All @@ -70,7 +70,7 @@ local ChicagoStyles = pl.tablex.merge(Bibliography.Style, {
italic(title),
". ",
optional(transEditor, ". "),
optional(address, ": "),
optional(location, ": "),
optional(pub, year and ", " or ". "),
optional(year, ". "),
optional(number, ". "),
Expand All @@ -85,7 +85,7 @@ local ChicagoStyles = pl.tablex.merge(Bibliography.Style, {
" ",
optional(transEditor, ". "),
optional(bibtype, ". "), -- "type" from BibTeX entry
optional(address, ": "),
optional(location, ": "),
optional(pub, ", "),
optional(year, ".")
end,
Expand Down

0 comments on commit 646e3a4

Please sign in to comment.