Module:grc-form of

local export = {}

local m_fun = require("Module:fun") local m_table = require("Module:table") local list_to_set = m_table.listToSet local invert = m_table.invert local keys_to_list = m_table.keysToList

local function quote(text) return "“" .. tostring(text) .. "”" end

local old_error = error local function error(msg, level) msg = string.gsub(msg, "'", "’") old_error(msg, level) end

local function errorf(...) error(string.format(...), 3) end

local info = setmetatable({}, {	__index = function(self, key)		local val = { type = key }		self[key] = val		return val	end })

local label_info = { ["first person"] = info.person, ["second person"] = info.person, ["third person"] = info.person, singular = info.number, dual = info.number, plural = info.number, present = info.tense, future = info.tense, perfect = info.tense, aorist = info.tense, imperfect = info.tense, pluperfect = info.tense, ["future perfect"] = info.tense, indicative = info.mood, subjunctive = info.mood, optative = info.mood, imperative = info.mood, infinitive = info.nonfinite, participle = info.nonfinite, active = info.voice, middle = info.voice, mediopassive = info.voice, passive = info.voice, nominative = info.case, vocative = info.case, accusative = info.case, genitive = info.case, dative = info.case, masculine = info.gender, feminine = info.gender, neuter = info.gender, m = info.gender, f = info.gender, n = info.gender, }

local abbreviations = { s = "singular", d = "dual", p = "plural", [1] = "first person", [2] = "second person", [3] = "third person", pres = "present", aor = "aorist", fut = "future", imperf = "imperfect", perf = "perfect", plup = "pluperfect", ind = "indicative", subj = "subjunctive", opt = "optative", imp = "imperative", inf = "infinitive", part = "participle", act = "active", mid = "middle", mp = "mediopassive", pass = "passive", masc = "masculine", nom = "nominative", m = "masculine", f = "feminine", n = "neuter", }

local valid_categories = { verb = list_to_set{ "person", "number", "tense", "mood", "voice", "nonfinite" }, adjective = list_to_set{ "gender", "case", "number" }, noun = list_to_set{ "number", "case" }, participle = list_to_set{ "gender", "case", "number", "tense", "voice" }, -- or treat as verb? infinitive = list_to_set{ "tense", "voice" }, -- or treat as verb? }

valid_categories.pronoun = valid_categories.noun -- Some pronouns have gender, some don't. valid_categories.demonstrative = valid_categories.adjective

-- No one part of speech has all these categories, but this order should hold true -- for all of them. local master_order = invert{ "gender", "case", "person", "number", "tense", "mood", "voice", "nonfinite" }

local function unrecognized_POS_error(POS) return error("The part of speech " .. quote(POS) .. " does not have a set of valid inflectional categories.", 3) end

function export.validate_labels(category_map, POS) local validation = valid_categories[POS] or unrecognized_POS_error(POS) local invalid_labels = m_fun.filter(		function(_, category)			return not validation[category]		end,		category_map) local first_key = next(invalid_labels) if first_key then local agreement = next(invalid_labels, first_key) and { "ies", "are" } or { "y", "is" } errorf("The categor%s %s %s not used by the part of speech %s.",			agreement[1], table.concat(m_fun.map(quote, keys_to_list(invalid_labels)), ", "), agreement[2], quote(POS)) end end

function export.resolve_abbr(labels) return m_fun.map(function(label)			return abbreviations[tonumber(label)] or abbreviations[label] or label		end,		labels) end

-- After resolution of abbreviations. -- Creates map from inflectional category to label: -- { "masculine", "nominative", "singular" } --		↓ -- { gender = "masculine", case = "nominative", number = "singular" } -- If there are two labels belonging to the same inflectional category, -- throws an error. local function make_category_map(labels) local category_map = {} local conflicts = {} for _, label in ipairs(labels) do		local type = label_info[label] and label_info[label].type if not type then error("The label " .. quote(label) .. " is not in the list of labels.") end if category_map[type] then if not conflicts[type] then conflicts[type] = { category_map[type] } end table.insert(conflicts[type], label) end category_map[type] = label end if next(conflicts) then conflicts = m_fun.mapIter(function(conflict, category)				return ("%s: %s"):format(quote(category), table.concat(m_fun.map(quote, conflict), ", "))			end,			pairs(conflicts)) error(string.format("There were multiple labels belonging to the same categor%s: %s.", conflicts[2] and "ies" or "y", table.concat(conflicts, "; "))) end return category_map end

local function capitalize(str) return string.gsub(str, "^.", string.upper) end

local get_position = m_fun.memoize(function (label)	return master_order[label_info[label].type] or		mw.log("no position for label " .. quote(label) .. " of type " .. quote(label_info[label].type) .. ".") end)

local function comp(label1, label2) return get_position(label1) < get_position(label2) end

function export.sort(labels, POS) table.sort(labels, comp) return labels end

function export.process(labels, POS) labels = export.resolve_abbr(labels) local category_map = make_category_map(labels, POS) if POS == "verb" then if category_map.nonfinite then POS, category_map.nonfinite = category_map.nonfinite, nil end end export.validate_labels(category_map, POS) export.sort(labels) return labels end

function export.show(frame) local params = { [1] = { list = true }, pos = {} }	local args = require("Module:parameters").process(frame.args, params) local labels = args[1] local POS = args.pos return table.concat(export.process(labels, POS), ", ") end

return export