summaryrefslogtreecommitdiff
path: root/biblio/citation-style-language/citeproc-node-sort.lua
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2022-08-19 03:00:56 +0000
committerNorbert Preining <norbert@preining.info>2022-08-19 03:00:56 +0000
commit1772be123f1cfa713b25548f6fec135e7ab339a3 (patch)
tree1ba6586dc52536e962cb28380567183f6f90bbc1 /biblio/citation-style-language/citeproc-node-sort.lua
parent982d5e88b736a798b356bf5cabe5e6c9b115f182 (diff)
CTAN sync 202208190300
Diffstat (limited to 'biblio/citation-style-language/citeproc-node-sort.lua')
-rw-r--r--biblio/citation-style-language/citeproc-node-sort.lua213
1 files changed, 129 insertions, 84 deletions
diff --git a/biblio/citation-style-language/citeproc-node-sort.lua b/biblio/citation-style-language/citeproc-node-sort.lua
index 1b82d21b55..876c3257da 100644
--- a/biblio/citation-style-language/citeproc-node-sort.lua
+++ b/biblio/citation-style-language/citeproc-node-sort.lua
@@ -8,57 +8,80 @@ local sort = {}
local unicode = require("unicode")
-local element = require("citeproc-element")
+local Element = require("citeproc-element").Element
+local Date = require("citeproc-node-date").Date
local names = require("citeproc-node-names")
-local date = require("citeproc-node-date")
+local InlineElement = require("citeproc-output").InlineElement
local util = require("citeproc-util")
-local Sort = element.Element:new()
+-- [Sorting](https://docs.citationstyles.org/en/stable/specification.html#sorting)
+local Sort = Element:derive("sort")
-function Sort:sort (items, context)
+function Sort:from_node(node)
+ local o = Sort:new()
+ o.children = {}
+
+ o:process_children_nodes(node)
+ o.sort_directions = {}
+ for i, key in ipairs(o.children) do
+ o.sort_directions[i] = (key.sort ~= "descending")
+ end
+ table.insert(o.sort_directions, true)
+
+ return o
+end
+
+function Sort:sort(items, state, context)
-- key_map = {
-- id1 = {key1, key2, ...},
-- id2 = {key1, key2, ...},
-- ...
-- }
- context.variable_attempt = {}
-
local key_map = {}
- local sort_directions = {}
+ local sort_directions = self.sort_directions
-- true: ascending
-- false: descending
- if not Sort.collator_obj then
- local lang = context.style.lang
+ if not Sort.collator then
+ local lang = context.engine.lang
local language = string.sub(lang, 1, 2)
-- It's 6 seconds slower to run the whole test-suite if these package
-- loading statements are put in the header.
- local ducet = require("lua-uca.lua-uca-ducet")
- local collator = require("lua-uca.lua-uca-collator")
- local languages = require("lua-uca.lua-uca-languages")
- local collator_obj = collator.new(ducet)
- if languages[language] then
- Sort.collator_obj = languages[language](collator_obj)
- else
- util.warning(string.format('Lcoale "%s" is not supported.', lang))
+ local uca_ducet = require("lua-uca.lua-uca-ducet")
+ local uca_collator = require("lua-uca.lua-uca-collator")
+ Sort.collator = uca_collator.new(uca_ducet)
+ if language ~= "en" then
+ local uca_languages = require("lua-uca.lua-uca-languages")
+ if uca_languages[language] then
+ Sort.collator = uca_languages[language](Sort.collator)
+ else
+ util.warning(string.format('Locale "%s" is not provided by lua-uca. The sorting order may be incorrect.', lang))
+ end
end
end
- for _, item in ipairs(items) do
- if not key_map[item.id] then
- key_map[item.id] = {}
-
- context.item = item
- for i, key in ipairs(self:query_selector("key")) do
- if sort_directions[i] == nil then
- local direction = (key:get_attribute("sort") ~= "descending")
- sort_directions[i] = direction
- end
- local value = key:render(item, context)
- table.insert(key_map[item.id], value)
+ -- TODO: optimize: use cached values for fixed keys
+ for i, item in ipairs(items) do
+ key_map[item.id] = {}
+
+ context.id = item.id
+ context.cite = item
+ context.reference = context.engine.registry.registry[item.id]
+
+ for j, key in ipairs(self.children) do
+ if context.reference then
+ context.sort_key = key
+ local key_str = key:eval(context.engine, state, context)
+ key_map[item.id][j] = key_str
+ else
+ -- The entry is missing
+ key_map[item.id][j] = false
end
end
+ -- To preserve the original order of items with same sort keys
+ -- sort_NameImplicitSortOrderAndForm.txt
+ table.insert(key_map[item.id], i)
end
-- util.debug(key_map)
@@ -66,6 +89,7 @@ function Sort:sort (items, context)
local function compare_entry(item1, item2)
return self.compare_entry(key_map, sort_directions, item1, item2)
end
+ -- util.debug(items)
table.sort(items, compare_entry)
return items
@@ -80,8 +104,8 @@ function Sort.compare(value1, value2)
end
function Sort.compare_strings(str1, str2)
- if Sort.collator_obj then
- return Sort.collator_obj:compare_strings(str1, str2)
+ if Sort.collator then
+ return Sort.collator:compare_strings(str1, str2)
else
return str1 < str2
end
@@ -109,77 +133,98 @@ function Sort.compare_entry(key_map, sort_directions, item1, item2)
end
end
-local Key = element.Element:new()
-
-function Key:render (item, context)
- context = self:process_context(context)
- context.options["name-as-sort-order"] = "all"
- context.sorting = true
- local variable = self:get_attribute("variable")
- local res = nil
- if variable then
- context.variable = variable
- local variable_type = util.variable_types[variable]
+local Key = Element:derive("key")
+
+function Key:new()
+ local o = Element.new(self)
+ Key.sort = "ascending"
+ return o
+end
+
+function Key:from_node(node)
+ local o = Key:new()
+ o:set_attribute(node, "sort")
+ o:set_attribute(node, "variable")
+ o:set_attribute(node, "macro")
+ o:set_number_attribute(node, "names-min")
+ o:set_number_attribute(node, "names-use-first")
+ o:set_bool_attribute(node, "names-use-last")
+ return o
+end
+
+function Key:eval(engine, state, context)
+ local res
+ if self.variable then
+ local variable_type = util.variable_types[self.variable]
if variable_type == "name" then
- res = self:_render_name(item, context)
+ res = self:eval_name(engine, state, context)
elseif variable_type == "date" then
- res = self:_render_date(item, context)
+ res = self:eval_date(context)
elseif variable_type == "number" then
- res = item[variable]
+ local value = context:get_variable(self.variable)
+ if type(value) == "string" and string.match(value, "%s+") then
+ value = tonumber(value)
+ end
+ res = value
else
- res = item[variable]
+ res = context:get_variable(self.variable)
+ if type(res) == "string" then
+ local inlines = InlineElement:parse(res, context)
+ res = context.format:output(inlines)
+ end
end
- else
- local macro = self:get_attribute("macro")
- if macro then
- res = self:get_macro(macro):render(item, context)
+ elseif self.macro then
+ local macro = context:get_macro(self.macro)
+ state:push_macro(self.macro)
+ local ir = macro:build_ir(engine, state, context)
+ state:pop_macro(self.macro)
+ if ir.name_count then
+ return ir.name_count
+ elseif ir.sort_key ~= nil then
+ return ir.sort_key
end
+ local output_format = context.format
+ local inlines = ir:flatten(output_format)
+ -- util.debug(inlines)
+ local str = output_format:output(inlines)
+ return str
end
if res == nil then
+ -- make table.insert(_, nil) work
res = false
- elseif type(res) == "table" and res._type == "RichText" then
- res = res:render(nil, context)
- end
- if type(res) == "string" then
- res = self._normalize_string(res)
end
return res
end
-function Key:_render_name (item, context)
- if not self.names then
- self.names = self:create_element("names", {}, self)
- names.Names:set_base_class(self.names)
- self.names:set_attribute("variable", context.options["variable"])
- self.names:set_attribute("form", "long")
+function Key:eval_name(engine, state, context)
+ if not self.name_inheritance then
+ self.name_inheritance = util.clone(context.name_inheritance)
end
- local res = self.names:render(item, context)
- return res
+ local name = context:get_variable(self.variable)
+ if not name then
+ return false
+ end
+ local ir = self.name_inheritance:build_ir(self.variable, nil, nil, engine, state, context)
+ if ir.name_count then
+ -- name count
+ return ir.name_count
+ end
+ local output_format = context.format
+ local inlines = ir:flatten(output_format)
+
+ local str = output_format:output(inlines)
+
+ return str
end
-function Key:_render_date (item, context)
+function Key:eval_date(context)
if not self.date then
- self.date = self:create_element("date", {}, self)
- date.Date:set_base_class(self.date)
- self.date:set_attribute("variable", context.options["variable"])
- self.date:set_attribute("form", "numeric")
+ self.date = Date:new()
+ self.date.variable = self.variable
+ self.date.form = "numeric"
+ self.date.date_parts = "year-month-day"
end
- local res = self.date:render(item, context)
- return res
-end
-function Key._normalize_string(str)
- str = unicode.utf8.lower(str)
- str = string.gsub(str, "[%[%]]", "")
- local words = {}
- for _, word in ipairs(util.split(str, " ")) do
- -- TODO: strip leading prepositions
- -- remove leading apostrophe on name particle
- word = string.gsub(word, "^" .. util.unicode["apostrophe"], "")
- table.insert(words, word)
- end
- str = table.concat(words, " ")
- str = string.gsub(str, util.unicode["apostrophe"], "'")
- return str
+ return self.date:render_sort_key(context.engine, nil, context)
end