Module:sla-noun

From Wiktionary, the free dictionary
Jump to navigation Jump to search

This module is used to create declensional tables for Proto-Slavic nouns.


local m_utilities = require("Module:utilities")
local com = require("Module:sla-common")

local export = {}

local lang = require("Module:languages").getByCode("sla-pro")

local rfind = mw.ustring.find
local rsubn = mw.ustring.gsub
local rmatch = mw.ustring.match
local rsplit = mw.text.split
local ulower = mw.ustring.lower
local uupper = mw.ustring.upper
local usub = mw.ustring.sub
local ulen = mw.ustring.len
local ugmatch = mw.ustring.gmatch

function export.show(frame)
	local SUBPAGENAME = mw.title.getCurrentTitle().subpageText
	
	local params = {
		[1] = {alias_of = 'recons'},
		recons = {default = SUBPAGENAME},
		
		n = {default = "sdp"},
		
		ap = {},
		
		g = {},
		
		stem = {},
	}
	
	local args = require("Module:parameters").process(frame:getParent().args, params)
	local gender = args["g"]
	local ap = args["ap"]
	local nom_sg = com.canon_decompose(args["recons"])
	local unom_sg = (lang:makeEntryName(nom_sg))
	local stem, desinence = com.auto_accent_and_check_accents(nom_sg, ap)
	local ustem, _ = com.split_stem_desinence(unom_sg)
	local num = {}
	
	if not args.n or not rfind(args.n, "^s?d?p?$") then
		error("Illegal number “" .. args.n .. "”. Possible values are “s”, “d”, “p”, “sd”, “dp”, “sp”, or “sdp”.")
	else
		num = rsplit(args.n, "")
	end

	if (nom_sg == "" or desinence == "") then
		error("Error: SUBPAGENAME=" .. SUBPAGENAME .. "Must only be used in the Reconstruction namespace with " .. lang:getCanonicalName() .. " reconstructions")
	end
	
	local stem_type = get_stem(args["stem"], desinence, gender, unom_sg)
	if stem_type == "soft a-stem" and desinence=="i" then -- ī-stem +j/+ьj (Module:sla-noun/data declensions["soft a-stem"] doesn't let check data.args["stem"], for some reason #data.args==0)
		stem = ( args["stem"] and args["stem"]=="ī/ьj" and ustem.."ьj" or com.iotate(ustem) )
	end
		
	if ap and ap ~= "a" and ap ~= "b" and ap ~= "c" then
		error("Accent paradigm “" .. ap .. "” must be either “a”, “b”, or “c”")
	end
	local forms, title, extracats
	local categories = {}
	local data = {
		args = args,
		gender = gender,
		desinence = desinence,
		ap = ap,
		nom_sg = nom_sg,
		unom_sg = unom_sg,	-- only used for *mati/*dъťi?
		stem = stem,
		ustem = ustem,
		footnotes = {},
		footnote_to_sym = {},
		next_notesym = "*"
	}
	
	local declensions = require("Module:sla-noun/data")
	
	if declensions[stem_type] then
		forms, title, extracats = declensions[stem_type](data)
	else
		error("Internal error -- unrecognized internal stem type '" .. stem_type .. "'")
	end

	if extracats then
		for _, cat in ipairs(extracats) do
			table.insert(categories, lang:getCanonicalName() .. " " .. cat)
		end
	end
	table.insert(categories, 1, lang:getCanonicalName() .. " " .. title .. " nouns")
	if ap then
		title = title .. ", accent paradigm " .. ap
		table.insert(categories, lang:getCanonicalName() .. " nominals with accent paradigm " .. ap)
	end
	
	if #num == 1 then
		if num[1] == 's' then
			title = title .. ', uncountable'
			table.insert(categories, lang:getCanonicalName() .. " singularia tantum")
		elseif num[1] == 'p' then
			title = title .. ', plural only'
			table.insert(categories, lang:getCanonicalName() .. " pluralia tantum")
		end
	end
	
	return make_table(forms, num, title, data.footnotes) .. m_utilities.format_categories(categories, lang)
end

function get_stem(st, desinence, gender, unom_sg)
	if st then -- explicitly specified stem
		if st == "o" then
			if "ъ" == desinence then
				return "hard masculine o-stem"
			elseif "ь" == desinence then
				return "soft masculine o-stem"
			elseif "o" == desinence then
				return "hard neuter o-stem"
			elseif "e" == desinence then
				return "soft neuter o-stem"
			else
				error("Unrecognized ending for o-stem")
			end
		elseif st == "a" then
			if "a" ~= desinence then
				error("Unrecognized ending for a-stem")
			end
			if rfind(unom_sg, "[cčďjľňřšťž]a$") or rfind(unom_sg, "dza$") then
				return "soft a-stem"
			else
				return "hard a-stem"
			end
		elseif st == "i" then
			if "ь" ~= desinence then
				error("Unrecognized ending for i-stem")
			end
			if gender == "m" then
				return "masculine i-stem"
			elseif gender == "f" then
				return "feminine i-stem"
			else
				error("Gender for i-stems must be specified through g= parameter, as \"m\" or \"f\"")
			end
		elseif st == "ī" or st=="ī/ьj" or st=="ī/j" then
			if "i" ~= desinence then
				error("Unrecognized ending for ī-stem")
			end
			return "soft a-stem" -- FIXME, remove this hack
		--consonant stems have several names in the literature, we handle all so that editors don't have to remember which one Wiktionary prefers
		elseif st == "n" then
			return "n-stem"
		elseif st == "nt" or st == "t" then
			return "nt-stem"
		elseif st == "r" then
			return "r-stem"
		elseif st == "s" then
			return "s-stem"
		elseif st == "u" then
			return "u-stem"
		elseif st == "v" or st == "ū" or st == "ъv" then
			return "v-stem"
		else
			error("Unrecognized stem class " .. st)
		end
	-- Autodetect common stem types on the basis of desinence, preceding
	-- consonant, and passed arguments (g=).
	-- hard masculine o-stems always and in -ъ, and we skip u-stems (which have stem= parameter provided)
	elseif "ъ" == desinence then
		return "hard masculine o-stem"
	-- soft masculine o-stems end in ь and are preceded by a soft, palatal consonant, except for such i-stems which will
	-- always have g= parameter specified
	elseif "ь" == desinence and not gender and (rfind(unom_sg, "[cčďjľňřšťž]ь$") or rfind(unom_sg, "dzь$")) then
		return "soft masculine o-stem"
	-- hard neuter o-stems always end in -o, and we skip s-stems in -o (which have stem= parameter provided)
	elseif "o" == desinence then
		return "hard neuter o-stem"
	-- soft neuter o-stems always end in -e and are preceded by a soft, platal consonant, and we skip s-stems in -e 
	-- (which have stem= parameter provided)
	elseif "e" == desinence and rfind(unom_sg, "[cčďjľňřšťž]e$") then
		return "soft neuter o-stem"
	-- soft a-stems are feminines and masculines (OCS junoša) that and in -a and are preceded by a soft, palatal consonant, or
	-- in -i (except for words *mati and *dъťi which are handled as r-stems)
	elseif (  "a" == desinence and ( rfind(unom_sg, "[cčďjľňřšťž]a$") or rfind(unom_sg, "dza$") )  ) 
									or
		   ( ("i" == desinence) and (unom_sg ~= "mati") and (unom_sg ~= "dъťi") ) then
		return "soft a-stem"
	-- hard a-stems are the ones that are not soft a-stems
	elseif "a" == desinence then
		return "hard a-stem"
	-- i-stems ending in -ь preceded by a hard, non-palatal consonant. Gender is mandatory to distinguish between masculine and
	-- feminine inflection. We skip n-stems (having stem= parameter specified), which are handled below.
	elseif "ь" == desinence then
		if gender == "m" then
			return "masculine i-stem"
		elseif gender == "f" then
			return "feminine i-stem"
		else
			error("Gender for i-stems must be specified through g= parameter, as “m” or “f”")
		end
	-- in case of r-stems, there are only two nouns, so the stem= parameter might as well be superfluous
	elseif (unom_sg == "mati") or (unom_sg == "dъťi") then
		return "r-stem"
	else
		error("Unable to autodetect stem type; must specify stem=, per [[WT:ASLA]]")
	end
end

local number_map = {
	s = "singular",
	d = "dual",
	p = "plural",
}

local case_map = {
	nom = "'''nominative'''",
	acc = "'''accusative'''",
	gen = "'''genitive'''",
	loc = "'''locative'''",
	dat = "'''dative'''",
	ins = "'''instrumental'''",
	voc = "'''vocative'''",
}

function make_header(forms, num, title)
	local lemma = forms.nom[num[1]]
	if type(lemma) ~= "table" then
		-- lemma remains
	elseif lemma.notesym then
		lemma = lemma[1]
	else
		local lemmavals = {}
		for _, item in ipairs(lemma) do
			if type(item) == "table" then
				table.insert(lemmavals, com.link_form(item[1]))
			else
				table.insert(lemmavals, com.link_form(item))
			end
		end
		lemma = table.concat(lemmavals, ", ")
	end
	local header = {
		'<div class="NavFrame" style="max-width: ' .. (13 * (1 + #num)) .. 'em">',
		'<div class="NavHead" style="background:#eff7ff">[[Template:sla-decl-noun|Declension]] of '
			.. lemma .. (title and " (" .. title .. ")" or "") .. '</div>',
		'<div class="NavContent">',
		'{| class="inflection-table" style="width: 100%; background: #F9F9F9; text-align: center"',
		'|-',
		'| style="width:15%; background: #d9ebff" |',
	}
	for _, n in ipairs(num) do
		table.insert(header, '! style="width: ' .. math.floor(100 / (1 + #num)) .. '%; background: #d9ebff;" | ' .. number_map[n])
	end
	return table.concat(header, "\n")
end

function make_row(forms, num, case)
	local row = {
		"|-",
		"| style=\"background-color: #eff7ff;\" | " .. case_map[case] .. ""
	}
	for _, n in ipairs(num) do
		table.insert(row, "| " .. com.link_form(forms[case][n]))
	end
	return table.concat(row, "\n")
end

function make_footer(footnotes)
	local footnote_text = (#footnotes > 0) and [===[
<div style="width:100%;text-align:left;background:#e4f1ff">
<div style="display:inline-block;text-align:left;padding-left:1em;padding-right:1em">
]===] .. table.concat(footnotes, "<br />") .. [===[
</div></div>
]===] or ""

	return "|}\n" .. footnote_text .. "</div></div>"
end

-- Make the table
function make_table(forms, num, title, footnotes)
	return table.concat(
		{
			make_header(forms, num, title),
			make_row(forms, num, "nom"),
			make_row(forms, num, "gen"),
			make_row(forms, num, "dat"),
			make_row(forms, num, "acc"),
			make_row(forms, num, "ins"),
			make_row(forms, num, "loc"),
			make_row(forms, num, "voc"),
			make_footer(footnotes),
		},
		"\n"
	)
end

return export