Module:snon-headword: Difference between revisions

From Linguifex
Jump to navigation Jump to search
Undo revision 502335 by Sware (talk)
No edit summary
 
(23 intermediate revisions by the same user not shown)
Line 19: Line 19:
}
}


local function do_inflection(data, forms, label, accel)
local function glossary_link(...)
if forms and #forms > 0 then
return m_hw_util.glossary_link(...)
forms.label = label
end
if accel then
 
forms.accel = accel
local function insert_inflection(data, terms, label, accel)
end
m_hw_util.insert_inflection {
table.insert(data.inflections, forms)
headdata = data,
end
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
end


-- The main entry point.
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
function export.show(frame)
local poscat = frame.args[1] or error("Part of speech has not been specified. Please pass parameter 1 to the module invocation.")
local poscat = frame.args[1] or error("Part of speech has not been specified. Please pass parameter 1 to the module invocation.")
Line 74: Line 76:
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------


local allowed_genders = m_table.listToSet(
local allowed_genders = m_table.listToSet{
    {"m", "f", "mf", "mfbysense", "mfequiv", "gneut", "n", "m-p", "f-p", "mf-p", "mfbysense-p", "mfequiv-p", "gneut-p", "n-p", "?", "?-p"}
"m", "f", "mf", "mfbysense", "mfequiv", "gneut", "n",
)
"m-p", "f-p", "mf-p", "mfbysense-p", "mfequiv-p", "gneut-p", "n-p",
"m-s", "f-s", "mf-s", "mfbysense-s", "mfequiv-s", "gneut-s", "n-s",
"?", "?-p", "?-s"
}
 
-- Extensible table for default plural endings by gender
local default_plural_endings = {
m = "ar",
f = "ir",
-- Easily add more later, e.g., n = "ur"
}


local function validate_genders(genders)
local function validate_genders(genders)
Line 87: Line 99:
end
end
end
end
end
-- Extracts the base gender ("m", "f", etc.) ignoring number suffixes like "-p" or "-s"
local function get_base_gender(g)
if type(g) == "table" then g = g.spec end
g = g:gsub("%-p$", ""):gsub("%-s$", "")
if g == "mf" then return {"m", "f"} end
if default_plural_endings[g] then return {g} end
return {}
end
end


Line 95: Line 116:
validate_genders(args[1])
validate_genders(args[1])
data.genders = args[1]
data.genders = args[1]
-- Check for specific genders and pluralia tantum.
-- Check for specific genders and pluralia tantum.
local base_genders_present = {}
for _, g in ipairs(args[1]) do
for _, g in ipairs(args[1]) do
if type(g) == "table" then
if type(g) == "table" then
Line 102: Line 125:
if g:find("-p$") then
if g:find("-p$") then
is_plurale_tantum = true
is_plurale_tantum = true
end
for _, bg in ipairs(get_base_gender(g)) do
base_genders_present[bg] = true
end
end
end
end


local lemma = data.pagename
local lemma = data.pagename
local plurals = {}
local plurals = {}
local function insert_noun_inflection(terms, label, accel)
m_hw_util.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
local function make_plural(term)
return {{term .. "arr"}, {term .. "ar", l = "VE"}}
end
if is_plurale_tantum then
if is_plurale_tantum then
Line 126: Line 139:
error("Can't specify plurals of plurale tantum " .. category_pos)
error("Can't specify plurals of plurale tantum " .. category_pos)
end
end
insert(data.inflections, {label = m_hw_util.glossary_link("plural only")})
else
else
plurals = m_hw_util.parse_term_list_with_modifiers {
plurals = m_hw_util.parse_term_list_with_modifiers {
Line 133: Line 145:
splitchar = ",",
splitchar = ",",
}
}
-- Check for special plural signals
-- Check for special plural signals
local mode = nil
local mode = nil
local pl1 = plurals[1]
local pl1 = plurals[1]
if pl1 and #pl1.term == 1 then
if pl1 and #pl1.term == 1 then
Line 149: Line 161:
end
end
end
end
 
if args.mut then
local mutation = {
["r"] = "radical", ["l"] = "lenite", ["e"] = "eclipse"
}
table.insert(data.inflections, {label = mutation[args.mut]})
end
if is_plurale_tantum then
if is_plurale_tantum then
-- both singular and plural
table.insert(data.inflections, {label = "sometimes " .. glossary_link("plural only") .. ", in variation"})
insert(data.inflections, {label = "sometimes " .. m_hw_util.glossary_link("plural only") .. ", in variation"})
end
end
if mode == "?" then
if mode == "?" then
-- Plural is unknown
table.insert(data.categories, langname .. " " .. data.pos_category .. " with unknown or uncertain plurals")
insert(data.categories, langname .. " " .. data.pos_category .. " with unknown or uncertain plurals")
elseif mode == "!" then
elseif mode == "!" then
-- Plural is not attested
table.insert(data.inflections, {label = "plural not attested"})
insert(data.inflections, {label = "plural not attested"})
table.insert(data.categories, langname .. " " .. data.pos_category .. " with unattested plurals")
insert(data.categories, langname .. " " .. data.pos_category .. " with unattested plurals")
if plurals[1] then
if plurals[1] then
error("Can't specify any plurals along with unattested plural code '!'")
error("Can't specify any plurals along with unattested plural code '!'")
end
end
elseif mode == "-" then
elseif mode == "-" then
-- Uncountable noun; may occasionally have a plural
table.insert(data.categories, langname .. " uncountable " .. data.pos_category)
insert(data.categories, langname .. " uncountable " .. data.pos_category)
 
-- If plural forms were given explicitly, then show "usually"
if plurals[1] then
if plurals[1] then
insert(data.inflections, {label = "usually " .. m_hw_util.glossary_link("uncountable")})
table.insert(data.inflections, {label = "usually " .. glossary_link("uncountable")})
insert(data.categories, langname .. " countable " .. data.pos_category)
table.insert(data.categories, langname .. " countable " .. data.pos_category)
else
else
insert(data.inflections, {label = m_hw_util.glossary_link("uncountable")})
table.insert(data.inflections, {label = glossary_link("uncountable")})
end
end
else
else
-- Countable or mixed countable/uncountable
if not plurals[1] and not is_proper then
if not plurals[1] and not is_proper then
plurals[1] = {term = "+"}
plurals[1] = {term = "+"}
end
end
if mode == "~" then
if mode == "~" then
-- Mixed countable/uncountable noun, always has a plural
table.insert(data.inflections, {label = glossary_link("countable") .. " and " .. glossary_link("uncountable")})
insert(data.inflections, {label = m_hw_util.glossary_link("countable") .. " and " .. m_hw_util.glossary_link("uncountable")})
table.insert(data.categories, langname .. " uncountable " .. data.pos_category)
insert(data.categories, langname .. " uncountable " .. data.pos_category)
table.insert(data.categories, langname .. " countable " .. data.pos_category)
insert(data.categories, langname .. " countable " .. data.pos_category)
elseif plurals[1] then
elseif plurals[1] then
-- Countable nouns
table.insert(data.categories, langname .. " countable " .. data.pos_category)
insert(data.categories, langname .. " countable " .. data.pos_category)
else
else
-- Uncountable nouns
table.insert(data.categories, langname .. " uncountable " .. data.pos_category)
insert(data.categories, langname .. " uncountable " .. data.pos_category)
end
end
end
end


-- Gather plurals, handling requests for default plurals.
local new_plurals = {}
local has_default_or_hash = false
for _, pl in ipairs(plurals) do
for _, pl in ipairs(plurals) do
if pl.term:find("^%+") or pl.term:find("#") then
if pl.term == "+" then
has_default_or_hash = true
local generated_any = false
break
-- Generate default plurals for all matched base genders
end
for bg, _ in pairs(base_genders_present) do
end
table.insert(new_plurals, {term = lemma .. default_plural_endings[bg]})
 
generated_any = true
if has_default_or_hash then
end
for _, pl in ipairs(plurals) do
-- Fallback if no specific gender mapped
if pl.term == "+" then
if not generated_any then
-- make_plural should never return nil
table.insert(new_plurals, {term = lemma .. "ar"})  
pl = make_plural(lemma)
end
end
else
table.insert(new_plurals, pl)
end
end
end
end
plurals = new_plurals


insert_noun_inflection(plurals, "plural", "p")
insert_inflection(data, plurals, "plural", "p")


if plurals[2] then
if plurals[2] then
insert(data.categories, langname .. " " .. data.pos_category .. " with multiple plurals")
table.insert(data.categories, langname .. " " .. data.pos_category .. " with multiple plurals")
end
end
 
-- Generalized handler for other gendered forms with specific default suffixes
local function process_gender_form(param, label, accel, default_suffix)
local forms = m_hw_util.parse_term_list_with_modifiers {
paramname = param,
forms = args[param],
splitchar = ",",
}
if forms[1] then
local new_forms = {}
for _, f in ipairs(forms) do
if f.term == "+" and default_suffix then
table.insert(new_forms, {term = lemma .. default_suffix})
else
table.insert(new_forms, f)
end
end
insert_inflection(data, new_forms, label, accel)
end
end
end
end
process_gender_form("m", "masculine", "m")
process_gender_form("f", "feminine", "f")
process_gender_form("mpl", "masculine plural", "m|p", default_plural_endings["m"])
process_gender_form("fpl", "feminine plural", "f|p", default_plural_endings["f"])
-- Extensible for new forms like neuter:
-- process_gender_form("n", "neuter", "n")
-- process_gender_form("npl", "neuter plural", "n|p", default_plural_endings["n"])
end
end


Line 224: Line 263:
[1] = {list = "g", disallow_holes = true, required = not is_proper, default = "?", type = "genders", flatten = true},
[1] = {list = "g", disallow_holes = true, required = not is_proper, default = "?", type = "genders", flatten = true},
[2] = {list = "pl", disallow_holes = true},
[2] = {list = "pl", disallow_holes = true},
["w"] = {list = true, disallow_holes = true},
["m"] = {list = true},
["f"] = {list = true},
["mpl"] = {list = true},
["fpl"] = {list = true},
["mut"] = {set = {"r", "l", "e"}}
}
}
end
end
Line 238: Line 281:
do_noun(args, data, "is proper noun")
do_noun(args, data, "is proper noun")
end,
end,
}
-----------------------------------------------------------------------------------------
--                                Adjectives                                          --
-----------------------------------------------------------------------------------------
local function do_adj(args, data)
local lemma = data.pagename
if args.inv then
table.insert(data.inflections, {label = glossary_link("invariable")})
table.insert(data.categories, langname .. " indeclinable " .. data.pos_category)
else
local function process_adj_form(param, default_suffix, label, accel)
local forms = m_hw_util.parse_term_list_with_modifiers {
paramname = param,
forms = args[param],
splitchar = ",",
}
if not forms[1] then
forms = {{term = lemma .. default_suffix}}
else
local new_forms = {}
for _, f in ipairs(forms) do
if f.term == "+" then
table.insert(new_forms, {term = lemma .. default_suffix})
else
table.insert(new_forms, f)
end
end
forms = new_forms
end
insert_inflection(data, forms, label, accel)
end
process_adj_form("comp", "ar", "comparative", "comp")
process_adj_form("sup", "ast", "superlative", "sup")
end
end
pos_functions["adjectives"] = {
params = {
["comp"] = {list = true, disallow_holes = true},
["sup"] = {list = true, disallow_holes = true},
["inv"] = {type = "boolean"},
},
func = do_adj,
}
-----------------------------------------------------------------------------------------
--                                    Verbs                                          --
-----------------------------------------------------------------------------------------
local function do_verb(args, data)
local lemma = data.pagename
if args.irr then
table.insert(data.categories, langname .. " irregular verbs")
else
local function process_verb_form(param, default_suffix, label)
local forms = m_hw_util.parse_term_list_with_modifiers {
paramname = param,
forms = args[param],
splitchar = ",",
}
if not forms[1] then
forms = {{term = lemma .. default_suffix}}
else
local new_forms = {}
for _, f in ipairs(forms) do
if f.term == "+" then
table.insert(new_forms, {term = lemma .. default_suffix})
else
table.insert(new_forms, f)
end
end
forms = new_forms
end
insert_inflection(data, forms, label)
end
process_verb_form("pres", "ir", "present")
process_verb_form("past", "adhir", "past")
process_verb_form("imp", "adhist", "imperative")
process_verb_form("fut", "ist", "future")
end
end
pos_functions["verbs"] = {
params = {
["pres"] = {list = true, disallow_holes = true},
["past"] = {list = true, disallow_holes = true},
["imp"] = {list = true, disallow_holes = true},
["fut"] = {list = true, disallow_holes = true},
["irr"] = {type = "boolean"},
},
func = do_verb,
}
}


return export
return export

Latest revision as of 18:58, 12 May 2026



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 m_hw_util = require("Module:headword 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 m_hw_util.glossary_link(...)
end

local function insert_inflection(data, terms, label, accel)
	m_hw_util.insert_inflection {
		headdata = data,
		terms = terms,
		label = label,
		accel = accel and {form = accel} or nil,
	}
end

-- The main entry point.
function export.show(frame)
	local poscat = frame.args[1] or error("Part of speech has not been specified. Please pass parameter 1 to the module invocation.")
	local parargs = frame:getParent().args

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

	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

	return require("Module:headword").full_headword(data)
end

-----------------------------------------------------------------------------------------
--                                          Nouns                                      --
-----------------------------------------------------------------------------------------

local allowed_genders = m_table.listToSet{
	"m", "f", "mf", "mfbysense", "mfequiv", "gneut", "n",
	"m-p", "f-p", "mf-p", "mfbysense-p", "mfequiv-p", "gneut-p", "n-p",
	"m-s", "f-s", "mf-s", "mfbysense-s", "mfequiv-s", "gneut-s", "n-s",
	"?", "?-p", "?-s"
}

-- Extensible table for default plural endings by gender
local default_plural_endings = {
	m = "ar",
	f = "ir",
	-- Easily add more later, e.g., n = "ur"
}

local function validate_genders(genders)
	for _, g in ipairs(genders) do
		if type(g) == "table" then
			g = g.spec
		end
		if not allowed_genders[g] then
			error("Unrecognized gender: " .. g)
		end
	end
end

-- Extracts the base gender ("m", "f", etc.) ignoring number suffixes like "-p" or "-s"
local function get_base_gender(g)
	if type(g) == "table" then g = g.spec end
	g = g:gsub("%-p$", ""):gsub("%-s$", "")
	if g == "mf" then return {"m", "f"} end
	if default_plural_endings[g] then return {g} end
	return {}
end

local function do_noun(args, data, is_proper)
	local is_plurale_tantum = false
	local category_pos = m_en_util.singularize(data.pos_category)

	validate_genders(args[1])
	data.genders = args[1]
	
	-- Check for specific genders and pluralia tantum.
	local base_genders_present = {}
	for _, g in ipairs(args[1]) do
		if type(g) == "table" then
			g = g.spec
		end
		if g:find("-p$") then
			is_plurale_tantum = true
		end
		
		for _, bg in ipairs(get_base_gender(g)) do
			base_genders_present[bg] = true
		end
	end

	local lemma = data.pagename
	local plurals = {}
	
	if is_plurale_tantum then
		if args[2][1] then
			error("Can't specify plurals of plurale tantum " .. category_pos)
		end
	else
		plurals = m_hw_util.parse_term_list_with_modifiers {
			paramname = {2, "pl"},
			forms = args[2],
			splitchar = ",",
		}
		
		-- Check for special plural signals
		local mode = nil
		local pl1 = plurals[1]
		if pl1 and #pl1.term == 1 then
			mode = pl1.term
			if mode == "?" or mode == "!" or mode == "-" or mode == "~" then
				pl1.term = nil
				if next(pl1) then
					error(("Can't specify inline modifiers with plural code '%s'"):format(mode))
				end
				table.remove(plurals, 1)  -- Remove the mode parameter
			elseif mode ~= "+" and mode ~= "#" then
				error(("Unexpected plural code '%s'"):format(mode))
			end
		end
		
		if args.mut then
			local mutation = {
				["r"] = "radical", ["l"] = "lenite", ["e"] = "eclipse"
			}
			table.insert(data.inflections, {label = mutation[args.mut]})
		end
		
		if is_plurale_tantum then
			table.insert(data.inflections, {label = "sometimes " .. glossary_link("plural only") .. ", in variation"})
		end
		
		if mode == "?" then
			table.insert(data.categories, langname .. " " .. data.pos_category .. " with unknown or uncertain plurals")
		elseif mode == "!" then
			table.insert(data.inflections, {label = "plural not attested"})
			table.insert(data.categories, langname .. " " .. data.pos_category .. " with unattested plurals")
			if plurals[1] then
				error("Can't specify any plurals along with unattested plural code '!'")
			end
		elseif mode == "-" then
			table.insert(data.categories, langname .. " uncountable " .. data.pos_category)
			if plurals[1] then
				table.insert(data.inflections, {label = "usually " .. glossary_link("uncountable")})
				table.insert(data.categories, langname .. " countable " .. data.pos_category)
			else
				table.insert(data.inflections, {label = glossary_link("uncountable")})
			end
		else
			if not plurals[1] and not is_proper then
				plurals[1] = {term = "+"}
			end
			if mode == "~" then
				table.insert(data.inflections, {label = glossary_link("countable") .. " and " .. glossary_link("uncountable")})
				table.insert(data.categories, langname .. " uncountable " .. data.pos_category)
				table.insert(data.categories, langname .. " countable " .. data.pos_category)
			elseif plurals[1] then
				table.insert(data.categories, langname .. " countable " .. data.pos_category)
			else
				table.insert(data.categories, langname .. " uncountable " .. data.pos_category)
			end
		end

		local new_plurals = {}
		for _, pl in ipairs(plurals) do
			if pl.term == "+" then
				local generated_any = false
				-- Generate default plurals for all matched base genders
				for bg, _ in pairs(base_genders_present) do
					table.insert(new_plurals, {term = lemma .. default_plural_endings[bg]})
					generated_any = true
				end
				-- Fallback if no specific gender mapped
				if not generated_any then
					table.insert(new_plurals, {term = lemma .. "ar"}) 
				end
			else
				table.insert(new_plurals, pl)
			end
		end
		plurals = new_plurals

		insert_inflection(data, plurals, "plural", "p")

		if plurals[2] then
			table.insert(data.categories, langname .. " " .. data.pos_category .. " with multiple plurals")
		end
	end

	-- Generalized handler for other gendered forms with specific default suffixes
	local function process_gender_form(param, label, accel, default_suffix)
		local forms = m_hw_util.parse_term_list_with_modifiers {
			paramname = param,
			forms = args[param],
			splitchar = ",",
		}
		if forms[1] then
			local new_forms = {}
			for _, f in ipairs(forms) do
				if f.term == "+" and default_suffix then
					table.insert(new_forms, {term = lemma .. default_suffix})
				else
					table.insert(new_forms, f)
				end
			end
			insert_inflection(data, new_forms, label, accel)
		end
	end

	process_gender_form("m", "masculine", "m")
	process_gender_form("f", "feminine", "f")
	process_gender_form("mpl", "masculine plural", "m|p", default_plural_endings["m"])
	process_gender_form("fpl", "feminine plural", "f|p", default_plural_endings["f"])
	-- Extensible for new forms like neuter:
	-- process_gender_form("n", "neuter", "n")
	-- process_gender_form("npl", "neuter plural", "n|p", default_plural_endings["n"])
end

local function get_noun_params(is_proper)
	return {
		[1] = {list = "g", disallow_holes = true, required = not is_proper, default = "?", type = "genders", flatten = true},
		[2] = {list = "pl", disallow_holes = true},
		["m"] = {list = true},
		["f"] = {list = true},
		["mpl"] = {list = true},
		["fpl"] = {list = true},
		["mut"] = {set = {"r", "l", "e"}}
	}
end

pos_functions["nouns"] = {
	params = get_noun_params(),
	func = do_noun,
}

pos_functions["proper nouns"] = {
	params = get_noun_params("is proper"),
	func = function(args, data)
		do_noun(args, data, "is proper noun")
	end,
}

-----------------------------------------------------------------------------------------
--                                 Adjectives                                          --
-----------------------------------------------------------------------------------------

local function do_adj(args, data)
	local lemma = data.pagename
	
	if args.inv then
		table.insert(data.inflections, {label = glossary_link("invariable")})
		table.insert(data.categories, langname .. " indeclinable " .. data.pos_category)
	else
		local function process_adj_form(param, default_suffix, label, accel)
			local forms = m_hw_util.parse_term_list_with_modifiers {
				paramname = param,
				forms = args[param],
				splitchar = ",",
			}
			if not forms[1] then
				forms = {{term = lemma .. default_suffix}}
			else
				local new_forms = {}
				for _, f in ipairs(forms) do
					if f.term == "+" then
						table.insert(new_forms, {term = lemma .. default_suffix})
					else
						table.insert(new_forms, f)
					end
				end
				forms = new_forms
			end
			insert_inflection(data, forms, label, accel)
		end

		process_adj_form("comp", "ar", "comparative", "comp")
		process_adj_form("sup", "ast", "superlative", "sup")
	end
end

pos_functions["adjectives"] = {
	params = {
		["comp"] = {list = true, disallow_holes = true},
		["sup"] = {list = true, disallow_holes = true},
		["inv"] = {type = "boolean"},
	},
	func = do_adj,
}

-----------------------------------------------------------------------------------------
--                                     Verbs                                           --
-----------------------------------------------------------------------------------------

local function do_verb(args, data)
	local lemma = data.pagename
	
	if args.irr then
		table.insert(data.categories, langname .. " irregular verbs")
	else
		local function process_verb_form(param, default_suffix, label)
			local forms = m_hw_util.parse_term_list_with_modifiers {
				paramname = param,
				forms = args[param],
				splitchar = ",",
			}
			if not forms[1] then
				forms = {{term = lemma .. default_suffix}}
			else
				local new_forms = {}
				for _, f in ipairs(forms) do
					if f.term == "+" then
						table.insert(new_forms, {term = lemma .. default_suffix})
					else
						table.insert(new_forms, f)
					end
				end
				forms = new_forms
			end
			insert_inflection(data, forms, label)
		end

		process_verb_form("pres", "ir", "present")
		process_verb_form("past", "adhir", "past")
		process_verb_form("imp", "adhist", "imperative")
		process_verb_form("fut", "ist", "future")
	end
end

pos_functions["verbs"] = {
	params = {
		["pres"] = {list = true, disallow_holes = true},
		["past"] = {list = true, disallow_holes = true},
		["imp"] = {list = true, disallow_holes = true},
		["fut"] = {list = true, disallow_holes = true},
		["irr"] = {type = "boolean"},
	},
	func = do_verb,
}

return export