summaryrefslogtreecommitdiff
path: root/biblio/citation-style-language/citeproc-bib.lua
diff options
context:
space:
mode:
Diffstat (limited to 'biblio/citation-style-language/citeproc-bib.lua')
-rw-r--r--biblio/citation-style-language/citeproc-bib.lua522
1 files changed, 0 insertions, 522 deletions
diff --git a/biblio/citation-style-language/citeproc-bib.lua b/biblio/citation-style-language/citeproc-bib.lua
deleted file mode 100644
index 7e24b1a6a5..0000000000
--- a/biblio/citation-style-language/citeproc-bib.lua
+++ /dev/null
@@ -1,522 +0,0 @@
---
--- Copyright (c) 2021-2022 Zeping Lee
--- Released under the MIT license.
--- Repository: https://github.com/zepinglee/citeproc-lua
---
-
---[[
- A naive implementation of a Bib(La)TeX dateabase (.bib) parser
- References:
- - http://mirrors.ctan.org/biblio/bibtex/base/btxdoc.pdf
- - http://mirrors.ctan.org/info/bibtex/tamethebeast/ttb_en.pdf
- - https://github.com/brechtm/citeproc-py/blob/master/citeproc/source/bibtex/bibparse.py
- - http://maverick.inria.fr/~Xavier.Decoret/resources/xdkbibtex/bibtex_summary.html
- - https://github.com/pcooksey/bibtex-js/blob/master/src/bibtex_js.js
---]]
-
-local bib = {}
-
-local unicode = require("unicode")
-bib.bib_data = require("citeproc-bib-data")
-
-local util = require("citeproc-util")
-
-
-function bib.parse(contents)
- local items = {}
- for item_contents in string.gmatch(contents, "(@%w+%b{})") do
- local item = bib.parse_item(item_contents)
- table.insert(items, item)
- end
- return items
-end
-
-function bib.parse_item(contents)
- contents = string.gsub(contents, "%s*\r?\n%s*", " ")
- local bib_type, id
- bib_type, id, contents = string.match(contents, "^@(%w+){([^%s,]+),%s*(.*)}$")
- if not id then
- return nil
- end
-
- local item = {id = id, ["citation-key"] = id}
-
- bib_type = string.lower(bib_type)
- local type_data = bib.bib_data.types[bib_type]
- if type_data and type_data.csl then
- item.type = type_data.csl
- else
- item.type = "document"
- end
-
- local bib_fields = bib.parse_fields(contents)
- -- util.debug(bib_fields)
-
- for bib_field, value in pairs(bib_fields) do
- local csl_field, csl_value = bib.convert_field(bib_field, value)
-
- if csl_field and not item[csl_field] then
- item[csl_field] = csl_value
- end
- end
-
- bib.process_special_fields(item, bib_fields)
-
- return item
-end
-
-function bib.parse_fields(contents)
- local fields = {}
- local field_patterns = {
- "^([^,}%s]+)%s*=%s*(%b{}),?%s*(.-)$",
- '^([^,}%s]+)%s*=%s*"([^"]*)",?%s*(.-)$',
- "^([^,}%s]+)%s*=%s*(%w+),?%s*(.-)$",
- }
-
- while #contents > 0 do
- local field, value, rest
- -- This pattern may fail in the case of `title = {foo\}bar}`.
- for pattern_index, pattern in ipairs(field_patterns) do
- field, value, rest = string.match(contents, pattern)
- if value then
- field = string.lower(field)
- if pattern_index == 1 then
- -- Strip braces "{}"
- value = string.sub(value, 2, -2)
- elseif pattern_index == 3 then
- if not string.match(value, "^%d+$") then
- local string_name = value
- local macro = bib.bib_data.macros[string_name]
- if macro then
- value = macro.value
- else
- util.warning(string.format('String name "%s" is undefined', string_name))
- end
- end
- end
- fields[field] = value
- break
- end
- end
- if value then
- contents = rest
- else
- break
- end
- end
- return fields
-end
-
-function bib.convert_field(bib_field, value)
- local field_data = bib.bib_data.fields[bib_field]
- if not field_data then
- return nil, nil
- end
- local csl_field = field_data.csl
- if not csl_field then
- return nil, nil
- end
-
- value = bib.unescape(bib_field, value)
-
- local field_type = field_data.type
- if field_type == "name" then
- value = bib.parse_names(value)
- elseif field_type == "date" then
- value = bib.parse_date(value)
- end
-
- if bib_field == "title" or bib_field == "booktitle" then
- -- TODO: check if the original title is in sentence case
- value = bib.convert_sentence_case(value)
- end
-
- if bib_field == "volume" or bib_field == "pages" then
- value = string.gsub(value, util.unicode["en dash"], "-")
- end
-
- return csl_field, value
-end
-
-function bib.unescape(field, str)
- str = string.gsub(str, "%-%-%-", util.unicode["em dash"])
- str = string.gsub(str, "%-%-", util.unicode["en dash"])
- str = string.gsub(str, "``", util.unicode["left double quotation mark"])
- str = string.gsub(str, "''", util.unicode["right double quotation mark"])
- str = string.gsub(str, "`", util.unicode["left single quotation mark"])
- str = string.gsub(str, "'", util.unicode["right single quotation mark"])
- -- TODO: unicode chars like \"{o}
- str = string.gsub(str, "\\#", "#")
- str = string.gsub(str, "\\%$", "$")
- str = string.gsub(str, "\\%%", "%")
- str = string.gsub(str, "\\&", "&")
- str = string.gsub(str, "\\{", "{")
- str = string.gsub(str, "\\}", "}")
- str = string.gsub(str, "\\_", "_")
- if field ~= "url" then
- str = string.gsub(str, "~", util.unicode["no-break space"])
- end
- str = string.gsub(str, "\\quad%s+", util.unicode["em space"])
- return str
-end
-
-function bib.convert_sentence_case(str)
- local res = ""
- local to_lower = false
- local brace_level = 0
- for _, code_point in utf8.codes(str) do
- local char = utf8.char(code_point)
- if to_lower and brace_level == 0 then
- char = unicode.utf8.lower(char)
- end
- if string.match(char, "%S") then
- to_lower = true
- end
- if char == "{" then
- brace_level = brace_level + 1
- char = ""
- elseif char == "}" then
- brace_level = brace_level - 1
- char = ""
- elseif char == ":" then
- to_lower = false
- end
- res = res .. char
- end
- return res
-end
-
-function bib.parse_names(str)
- -- "{International Federation of Library Association and Institutions}"
- local names = {}
- local brace_level = 0
- local name = ""
- local last_word = ""
- for i = 1, #str do
- local char = string.sub(str, i, i)
- if char == " " then
- if brace_level == 0 and last_word == "and" then
- table.insert(names, name)
- name = ""
- else
- if name ~= "" then
- name = name .. " "
- end
- name = name .. last_word
- end
- last_word = ""
- else
- last_word = last_word .. char
- if char == "{" then
- brace_level = brace_level + 1
- elseif char == "}" then
- brace_level = brace_level - 1
- end
- end
- end
-
- if name ~= "" then
- name = name .. " "
- end
- name = name .. last_word
- table.insert(names, name)
-
- for i, name in ipairs(names) do
- names[i] = bib.parse_single_name(name)
- end
- return names
-end
-
-function bib.parse_single_name(str)
- local literal = string.match(str, "^{(.*)}$")
- if literal then
- return {
- literal = literal,
- }
- end
-
- local name_parts = util.split(str, ",%s*")
- if #name_parts > 1 then
- return bib.parse_revesed_name(name_parts)
- else
- return bib.parse_non_revesed_name(str)
- end
-end
-
-function bib.parse_revesed_name(name_parts)
- local name = {}
- local von, last, jr, first
- if #name_parts == 2 then
- first = name_parts[2]
- elseif #name_parts >= 3 then
- jr = name_parts[2]
- first = name_parts[3]
- end
- if first and first ~= "" then
- name.given = first
- end
- if jr and jr ~= "" then
- name.suffix = jr
- end
-
- last = name_parts[1]
- local words = util.split(last)
- local index = #words - 1
- while index > 0 and string.match(words[index], "^%L") do
- index = index - 1
- end
- name.family = util.concat(util.slice(words, index + 1), " ")
- if index >= 1 then
- von = util.concat(util.slice(words, 1, index), " ")
- name["non-dropping-particle"] = von
- end
- return name
-end
-
-function bib.parse_non_revesed_name(str)
- local name = {}
- local words = util.split(str)
-
- local index = 1
- -- TODO: case determination for pseudo-characters (e.g., "\bb{BB}")
- while index < #words and string.match(words[index], "^%L") do
- index = index + 1
- end
- if index > 1 then
- name.given = util.concat(util.slice(words, 1, index - 1), " ")
- end
-
- local particle_start_index = index
- index = #words - 1
- while index >= particle_start_index and string.match(words[index], "^%L") do
- index = index - 1
- end
- if index >= particle_start_index then
- local particles = util.slice(words, particle_start_index, index)
- -- TODO: distiguish dropping and non-dropping particles
- name["non-dropping-particle"] = util.concat(particles, " ")
- end
- name.family = util.concat(util.slice(words, index + 1), " ")
-
- return name
-end
-
-function bib.parse_date(str)
- local date_range = util.split(str, "/")
- if #date_range == 1 then
- date_range = util.split(str, util.unicode["en dash"])
- end
-
- local literal = { literal = str }
-
- if #date_range > 2 then
- return literal
- end
-
- local date = {}
- date["date-parts"] = {}
- for _, date_part in ipairs(date_range) do
- local date_ = bib.parse_single_date(date_part)
- if not date_ then
- return literal
- end
- table.insert(date["date-parts"], date_)
- end
- return date
-end
-
-function bib.parse_single_date(str)
- local date = {}
- for _, date_part in ipairs(util.split(str, "%-")) do
- if not string.match(date_part, "^%d+$") then
- return nil
- end
- table.insert(date, tonumber(date_part))
- end
- return date
-end
-
-
-function bib.process_special_fields(item, bib_fields)
- -- Default entry type `document`
- if item.type == "document" then
- if item.URL then
- item.type = "webpage"
- else
- item.type = "article"
- end
- end
-
- -- event-title: for compatibility with CSL v1.0.1 and earlier versions
- if item["event-title"] then
- item.event = item["event-title"]
- end
-
- -- issued date
- if bib_fields.year and not item.issued then
- item.issued = bib.parse_date(bib_fields.year)
- end
- local month = bib_fields.month
- if month and string.match(month, "^%d+$") then
- if item.issued and item.issued["date-parts"] and
- item.issued["date-parts"][1] and
- item.issued["date-parts"][1][2] == nil then
- item.issued["date-parts"][1][2] = tonumber(month)
- end
- end
-
- -- language: convert `babel` language to ISO 639-1 language code
- if not item.language and bib_fields.language then
- item.language = bib_fields.language
- end
- if item.language then
- local language_code = bib.babel_locale_mapping[item.language]
- if language_code then
- item.language = language_code
- end
- end
- -- if not item.language then
- -- if util.has_cjk_char(item.title) then
- -- item.language = "zh"
- -- end
- -- end
-
- -- number
- if item.number then
- if item.type == "article-journal" or item.type == "article-magazine" or item.type == "article-newspaper" or item.type == "periodical" then
- if not item.issue then
- item.issue = item.number
- item.number = nil
- end
- elseif item["collection-title"] and not item["collection-number"] then
- item["collection-number"] = item.number
- item.number = nil
- end
- end
-
- -- PMID
- if bib_fields.eprint and string.lower(bib_fields.eprinttype) == "pubmed" and not item.PMID then
- item.PMID = bib_fields.eprint
- end
-
-end
-
-
-bib.babel_locale_mapping = {
- acadian = "fr-CA",
- american = "en-US",
- australian = "en-AU",
- afrikaans = "af-ZA",
- albanian = "sq-AL",
- amharic = "am-ET",
- arabic = "ar",
- armenian = "hy-AM",
- asturian = "ast-ES",
- austrian = "de-AT",
- bahasa = "id-ID",
- bahasai = "id-ID",
- bahasam = "id-ID",
- basque = "eu-ES",
- bengali = "bn-BD",
- bgreek = "el-GR",
- brazil = "pt-BR",
- brazilian = "pt-BR",
- breton = "br-FR",
- british = "en-GB",
- bulgarian = "bg-BG",
- canadian = "en-CA",
- canadien = "fr-CA",
- catalan = "ca-AD",
- coptic = "cop",
- croatian = "hr-HR",
- czech = "cs-CZ",
- danish = "da-DK",
- divehi = "dv-MV",
- dutch = "nl-NL",
- english = "en-US",
- esperanto = "eo-001",
- estonian = "et-EE",
- ethiopia = "am-ET",
- farsi = "fa-IR",
- finnish = "fi-FI",
- francais = "fr-FR",
- french = "fr-FR",
- frenchle = "fr-FR",
- friulan = "fur-IT",
- galician = "gl-ES",
- german = "de-DE",
- germanb = "de-DE",
- greek = "el-GR",
- hebrew = "he-IL",
- hindi = "hi-IN",
- ibygreek = "el-CY",
- icelandic = "is-IS",
- indon = "id-ID",
- indonesia = "id-ID",
- interlingua = "ia-FR",
- irish = "ga-IE",
- italian = "it-IT",
- japanese = "ja-JP",
- kannada = "kn-IN",
- lao = "lo-LA",
- latin = "la-Latn",
- latvian = "lv-LV",
- lithuanian = "lt-LT",
- lowersorbian = "dsb-DE",
- lsorbian = "dsb-DE",
- magyar = "hu-HU",
- malay = "id-ID",
- malayalam = "ml-IN",
- marathi = "mr-IN",
- meyalu = "id-ID",
- mongolian = "mn-Cyrl",
- naustrian = "de-AT",
- newzealand = "en-NZ",
- ngerman = "de-DE",
- nko = "ha-NG",
- norsk = "nb-NO",
- norwegian = "nn-NO",
- nynorsk = "nn-NO",
- occitan = "oc-FR",
- piedmontese = "pms-IT",
- pinyin = "pny",
- polish = "pl-PL",
- polutonikogreek = "el-GR",
- portuges = "pt-PT",
- portuguese = "pt-PT",
- romanian = "ro-RO",
- romansh = "rm-CH",
- russian = "ru-RU",
- samin = "se-NO",
- sanskrit = "sa-IN",
- scottish = "gd-GB",
- serbian = "sr-Latn",
- serbianc = "sr-Cyrl",
- slovak = "sk-SK",
- slovene = "sl-SI",
- slovenian = "sl-SI",
- spanish = "es-ES",
- swedish = "sv-SE",
- swiss = "de-CH",
- swissgerman = "de-CH",
- nswissgerman = "de-CH",
- syriac = "syc",
- tamil = "ta-IN",
- telugu = "te-IN",
- thai = "th-TH",
- thaicjk = "th-TH",
- tibetan = "bo-CN",
- turkish = "tr-TR",
- turkmen = "tk-TM",
- ukrainian = "uk-UA",
- urdu = "ur-IN",
- UKenglish = "en-UK",
- uppersorbian = "hsb-DE",
- USenglish = "en-US",
- usorbian = "hsb-DE",
- vietnamese = "vi-VN",
- welsh = "cy-GB",
-}
-
-
-return bib