Documentation for this module may be created at Module:snon-headword/doc

local export = {}
local pos_functions = {}

local m_links = require("Module:links")
local m_table = require("Module:table")
local m_en_util = require("Module:en-utilities")

local lang = require("Module:languages").getByCode("snon")
local langname = lang:getCanonicalName()

local PAGENAME = mw.loadData("Module:headword/data").pagename

local suffix_categories = {
	["adjectives"] = true,
	["adverbs"] = true,
	["nouns"] = true,
	["verbs"] = true,
}

local function glossary_link(...)
	return require("Module:headword utilities").glossary_link(...)
end

local function do_inflection(data, forms, label, accel)
	if forms and #forms > 0 then
		forms.label = label
		if accel then
			forms.accel = accel
		end
		table.insert(data.inflections, forms)
	end
end

-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)

	local poscat = frame.args[1]
		or error("Plural part of speech e.g. 'nouns' has not been specified. Please pass parameter 1 to the module invocation.")

	local params = {
		["head"] = {list = true, disallow_holes = true},
	}

	if pos_functions[poscat] then
		for key, val in pairs(pos_functions[poscat].params) do
			params[key] = val
		end
	end

	local args = require("Module:parameters").process(parargs, params)

	local heads = args.head
	if #heads == 0 then
		heads = {pagename}
	end

	local data = {
		lang = lang,
		pos_category = poscat,
		categories = {},
		heads = heads,
		genders = {},
		inflections = {},
		pagename = pagename,
	}

	if pos_functions[poscat] then
		pos_functions[poscat].func(args, data)
	end
	
	if params.nomut then
		table.insert(data.inflections, {label = "not mutable"})
		table.insert(data.categories, langname .. " non-mutable terms")
	end
	
	return require("Module:headword").full_headword(data)
end

local allowed_genders = {
	["m"] = true,
	["f"] = true,
	["mf"] = true,
	["mfbysense"] = true,
	["p"] = true,
	["m-p"] = true,
	["f-p"] = true,
	["mf-p"] = true,
	["mfbysense-p"] = true,
}

local function noun_params(args)
	local params = {
		[1] = {alias_of = "g", list = false},
		["g"] = {list = true}, --gender(s)
		["f"] = {list = true}, --feminine form(s)
		["m"] = {list = true}, --masculine form(s)
		["nomut"] = {type = "boolean"},
	}
	if args[1] and args[1]:find("p$") then
		params[2] = {alias_of = "sg", list = false}
		params["sg"] = {list = true}
		params["msg"] = {list = true}
		params["fsg"] = {list = true}
	else
		params[2] = {alias_of = "pl", list = false}
		params["pl"] = {list = true}
	end
	return params
end

local function do_nouns(pos, args, data)
	local genders = {}
	for _, g in ipairs(args.g) do
		if not allowed_genders[g] then
			error("Unrecognized gender: " .. g)
		end
		table.insert(genders, g)
	end

	local plpos = m_en_util.pluralize(pos)

	-- Check for special plural signals
	local mode = nil

	if args.pl then
		-- not a plurale tantum
		if args.pl[1] == "?" or args.pl[1] == "!" or args.pl[1] == "-" or args.pl[1] == "~" or args.pl[1] == "#" then
			mode = args.pl[1]
			table.remove(args.pl, 1)  -- Remove the mode parameter
		end

		local countable, uncountable
		if mode == "?" then
			-- Plural is unknown
			table.insert(data.categories, langname .. " " .. plpos .. " with unknown or uncertain plurals")
		elseif mode == "!" then
			-- Plural is not attested
			table.insert(data.inflections, {label = "plural not attested"})
			table.insert(data.categories, langname .. " " .. plpos .. " with unattested plurals")
			return
		elseif mode == "-" then
			-- Uncountable noun; may occasionally have a plural
			uncountable = true
			-- If plural forms were given explicitly, then show "usually"
			if #args.pl > 0 then
				table.insert(data.inflections, {label = "usually " .. glossary_link("uncountable")})
				countable = true
			else
				table.insert(data.inflections, {label = glossary_link("uncountable")})
			end
		elseif mode == "~" then
			-- Mixed countable/uncountable noun, always has a plural
			table.insert(data.inflections, {label = glossary_link("countable") .. " and " .. glossary_link("uncountable")})
			uncountable = true
			countable = true
		elseif mode == "#" or pos == "noun" then
			-- Countable nouns; the default for regular nouns but not proper nouns
			if mode == "#" then
				table.insert(data.inflections, {label = glossary_link("countable")})
			end
			countable = true
		end

		if countable then
			table.insert(data.categories, langname .. " countable " .. plpos)
		end
		if uncountable and pos == "noun" then
			table.insert(data.categories, langname .. " uncountable " .. plpos)
		end

		if #args.pl > 0 then
			local plurals = {}

			for _, pl in ipairs(args.pl) do
				m_table.insertIfNot(plurals, pl)
			end

			do_inflection(data, plurals, "plural", {form = "p"})
		end
	else
		-- plurale tantum or "plural-basic" lemma (cases where the plural is the basic lemma and the
		-- singular is derived from it)
		local function has_singular(sgargs)
			return #sgargs > 0 and sgargs[1] ~= "-"
		end
		local new_g = {}
		for _, g in ipairs(genders) do
			if g ~= "p" then
				g = g:gsub("%-p$", "")
				table.insert(new_g, g)
			end
		end
		genders = nil
		if has_singular(args.sg) or has_singular(args.msg) or has_singular(args.fsg) then
			table.insert(data.inflections, {label = glossary_link("plural")})
			table.insert(data.categories, langname .. " plural-basic " .. plpos)
		end
		local function do_singular(sgargs, label, accel, genders)
			if sgargs then
				if sgargs[1] == "-" then
					table.insert(data.inflections, {label = "no " .. label})
				else
					if genders then
						for i, sgarg in ipairs(sgargs) do
							sgargs[i] = {term = sgarg, genders = genders}
						end
					end
					do_inflection(data, sgargs, label, accel)
				end
			end
		end
		do_singular(args.sg, glossary_link("singular"), {form = "singular"}, new_g)
		do_singular(args.msg, "masculine " .. glossary_link("singular"), nil, {"m"})
		do_singular(args.fsg, "feminine " .. glossary_link("singular"), nil, {"f"})
	end
	do_inflection(data, args.f, "feminine")
	do_inflection(data, args.m, "masculine")
	do_inflection(data, args.dim, glossary_link("diminutive"))
	data.genders = genders
end

pos_functions["nouns"] = {
	params = noun_params,
	func = function(args, data)
		return do_nouns("noun", args, data)
	end,
}

pos_functions["proper nouns"] = {
	params = noun_params,
	func = function(args, data)
		return do_nouns("proper noun", args, data)
	end,
}

local function pos_with_gender()
	return {
		params = {
			["g"] = {list = true},
			["mut"] = {type = "boolean"},
		},
		func = function(args, data)
			data.genders = args["g"]
		end,
	}
end

pos_functions.numerals = pos_with_gender()
pos_functions["adjective forms"] = pos_with_gender()
pos_functions["determiner forms"] = pos_with_gender()
pos_functions["noun forms"] = pos_with_gender()
pos_functions["noun plural forms"] = pos_with_gender()
pos_functions["numeral forms"] = pos_with_gender()
pos_functions["pronoun forms"] = pos_with_gender()
pos_functions["singulatives"] = pos_with_gender()
pos_functions["verb forms"] = pos_with_gender()

return export