summaryrefslogtreecommitdiff
path: root/biblio/citation-style-language/citeproc-element.lua
diff options
context:
space:
mode:
Diffstat (limited to 'biblio/citation-style-language/citeproc-element.lua')
-rw-r--r--biblio/citation-style-language/citeproc-element.lua161
1 files changed, 141 insertions, 20 deletions
diff --git a/biblio/citation-style-language/citeproc-element.lua b/biblio/citation-style-language/citeproc-element.lua
index 956095e622..65d836c646 100644
--- a/biblio/citation-style-language/citeproc-element.lua
+++ b/biblio/citation-style-language/citeproc-element.lua
@@ -6,7 +6,7 @@
local element = {}
-local context
+local l = require("lpeg")
local ir_node
local output
local util
@@ -21,6 +21,7 @@ else
util = require("citeproc.util")
end
+local GroupVar = ir_node.GroupVar
local SeqIr = ir_node.SeqIr
local InlineElement = output.InlineElement
@@ -144,7 +145,7 @@ end
function Element:build_children_ir(engine, state, context)
local child_irs = {}
local ir_sort_key
- local group_var = "plain"
+ local group_var = GroupVar.Plain
if self.children then
for _, child_element in ipairs(self.children) do
local child_ir = child_element:build_ir(engine, state, context)
@@ -152,8 +153,8 @@ function Element:build_children_ir(engine, state, context)
if child_ir.sort_key ~= nil then
ir_sort_key = child_ir.sort_key
end
- if child_ir.group_var == "important" then
- group_var = "important"
+ if child_ir.group_var == GroupVar.Important then
+ group_var = GroupVar.Important
end
table.insert(child_irs, child_ir)
end
@@ -163,7 +164,7 @@ function Element:build_children_ir(engine, state, context)
ir.sort_key = ir_sort_key
ir.group_var = group_var
if #child_irs == 0 then
- ir.group_var = "missing"
+ ir.group_var = GroupVar.Missing
else
ir.group_var = group_var
end
@@ -178,7 +179,7 @@ function Element:build_group_ir(engine, state, context)
local irs = {}
local name_count
local ir_sort_key
- local group_var = "UnresolvedPlain"
+ local group_var = GroupVar.UnresolvedPlain
for _, child_element in ipairs(self.children) do
-- util.debug(child_element.element_name)
@@ -193,13 +194,13 @@ function Element:build_group_ir(engine, state, context)
-- b) all variables that are called are empty. This accommodates
-- descriptive cs:text and `cs:label` elements.
local child_group_var = child_ir.group_var
- if child_group_var == "important" then
- group_var = "important"
- elseif child_group_var == "plain" and group_var == "UnresolvedPlain" then
- group_var = "plain"
- elseif child_group_var == "missing" and child_ir._type ~= "YearSuffix" then
- if group_var == "plain" or group_var == "UnresolvedPlain" then
- group_var = "missing"
+ if child_group_var == GroupVar.Important then
+ group_var = GroupVar.Important
+ elseif child_group_var == GroupVar.Plain and group_var == GroupVar.UnresolvedPlain then
+ group_var = GroupVar.Plain
+ elseif child_group_var == GroupVar.Missing and child_ir._type ~= "YearSuffix" then
+ if group_var == GroupVar.Plain or group_var == GroupVar.UnresolvedPlain then
+ group_var = GroupVar.Missing
end
end
@@ -220,8 +221,8 @@ function Element:build_group_ir(engine, state, context)
-- A non-empty nested cs:group is treated as a non-empty variable for the
-- puropses of determining suppression of the outer cs:group.
- if #irs > 0 and group_var == "plain" then
- group_var = "important"
+ if #irs > 0 and group_var == GroupVar.Plain then
+ group_var = GroupVar.Important
end
local ir = SeqIr:new(irs, self)
@@ -374,16 +375,12 @@ end
function Element:format_number(number, variable, form, context)
- -- if not util.is_numeric(number) then
- -- number = string.gsub(number, "\\%-", "-")
- -- return number
- -- end
number = util.strip(number)
if variable == "locator" then
variable = context:get_variable("label")
end
form = form or "numeric"
- local number_part_list = self:split_number_parts(number, context)
+ local number_part_list = self:split_number_parts_lpeg(number, context)
-- {
-- {"1", "", " & "}
-- {"5", "8", ", "}
@@ -422,9 +419,129 @@ function Element:format_number(number, variable, form, context)
res = res .. number_parts[3]
end
return res
+end
+
+---comment
+---@param number any
+---@param context any
+function Element:parse_number_tokens(number, context)
+ local and_text = "and"
+ local and_symbol = "&"
+ if context then
+ and_text = context.locale:get_simple_term("and") or "and"
+ and_symbol = context.locale:get_simple_term("and", "symbol") or "&"
+ end
+ -- util.debug(and_symbol)
+
+ local space = l.S(" \t\r\n")
+ local delimiter_patt = space^0 * l.P(",") * space^0 * l.P(and_text) * space^1
+ + space^0 * l.P(",") * space^0 * l.P(and_symbol) * space^0
+ + space^0 * l.P(",") * space^0 * l.P("&") * space^0
+ + space^1 * l.P(and_text) * space^1
+ + space^0 * l.P(and_symbol) * space^0
+ + space^0 * l.P("&") * space^0
+ + space^0 * l.P(",") * space^0
+ + space^0 * l.P("-") * space^0
+ + space^0 * l.P(util.unicode["en dash"]) * space^0
+ local delimiter = l.C(delimiter_patt^1) / function (delimiter)
+ return {
+ type = "delimiter",
+ value = delimiter,
+ }
+ end
+ local token_patt = l.C((l.P("\\-") + 1 - delimiter_patt)^1) / function (token)
+ return {
+ type = "string",
+ value = token,
+ }
+ end
+ local grammer = l.Ct(token_patt * (delimiter * token_patt)^0)
+ local tokens = grammer:match(number)
+ -- util.debug(tokens)
+
+ for i, token in ipairs(tokens) do
+ if token.type == "string" then
+ token.value = string.gsub(token.value, "\\%-", "-")
+ elseif token.type == "delimiter" then
+ token.value = string.gsub(token.value, "%s*,%s*", ", ")
+ token.value = string.gsub(token.value, "&", and_symbol)
+ token.value = string.gsub(token.value, "%s*&%s*", " & ")
+ end
+ end
+ local stop_index = 0
+ for i, token in ipairs(tokens) do
+ if token.type == "string" then
+ if string.match(token.value, "^%w*%d+%w*$")
+ or string.match(token.value, "^[mdclxvi]+$")
+ or string.match(token.value, "^[MDCLXVI]+$") then
+ token.type = "number"
+ else
+ stop_index = i
+ if i > 1 and tokens[i-1].type == "delimiter" then
+ stop_index = i - 1
+ end
+ break
+ end
+ elseif token.type == "delimiter" then
+ token.delimiter_type = "and"
+ if string.match(token.value, "^%s*-%s*$")
+ or string.match(token.value, "^%s*–%s*$") then
+ token.delimiter_type = "range"
+ if i > 2 and tokens[i-2].delimiter_type == "range" then
+ stop_index = i
+ break
+ end
+ end
+ end
+ end
+
+ if stop_index > 0 then
+ local token = tokens[stop_index]
+ token.type = "string"
+ for i = stop_index + 1, #tokens do
+ token.value = token.value .. tokens[i].value
+ end
+ for i = #tokens, stop_index + 1, -1 do
+ table.remove(tokens, i)
+ end
+ end
+
+ return tokens
+end
+
+-- Returns something like
+-- {
+-- {"1", "", " & "}
+-- {"5", "8", ", "}
+-- }
+function Element:split_number_parts_lpeg(number, context)
+ local tokens = self:parse_number_tokens(number, context)
+ local number_parts = {}
+ for i, token in ipairs(tokens) do
+ if token.type == "number" then
+ if i == 1 or tokens[i-1].delimiter_type == "and" then
+ table.insert(number_parts, {token.value, "", ""})
+ else
+ number_parts[#number_parts][2] = token.value
+ end
+ elseif token.type == "delimiter" then
+ if token.delimiter_type == "and" then
+ number_parts[#number_parts][3] = token.value
+ end
+ else
+ if #number_parts > 0 then
+ number_parts[#number_parts][3] = token.value
+ else
+ table.insert(number_parts, {token.value, "", ""})
+ end
+ end
+ end
+ -- util.debug(number_parts)
+ return number_parts
end
+
function Element:split_number_parts(number, context)
-- number = string.gsub(number, util.unicode["en dash"], "-")
local and_symbol
@@ -440,6 +557,10 @@ function Element:split_number_parts(number, context)
delim = ", "
elseif delim == "&" then
delim = and_symbol or " & "
+ elseif delim == "and" then
+ delim = " and "
+ elseif delim == "et" then
+ delim = " et "
end
local start = single_number
local stop = ""