Module:User:Benwing2/ky-noun

Hello, you have come here looking for the meaning of the word Module:User:Benwing2/ky-noun. In DICTIOUS you will not only get to know all the dictionary meanings for the word Module:User:Benwing2/ky-noun, but we will also tell you about its etymology, its characteristics and you will know how to say Module:User:Benwing2/ky-noun in singular and plural. Everything you need to know about the word Module:User:Benwing2/ky-noun you have here. The definition of the word Module:User:Benwing2/ky-noun will help you to be more precise and correct when speaking or writing your texts. Knowing the definition ofModule:User:Benwing2/ky-noun, as well as those of other words, enriches your vocabulary and provides you with more and better linguistic resources.


local export = {}

--[=[

Authorship: Ben Wing <benwing2>

]=]

--[=[

TERMINOLOGY:

-- "slot" = A particular combination of case/number.
	 Example slot names for nouns are "gen_s" (genitive singular) and
	 "abl_2s_inform_mpos" (ablative 2nd-singular informal multiple-possession).
	 Each slot is filled with zero or more forms.

-- "form" = The declined Kyrgyz form representing the value of a given slot.

-- "lemma" = The dictionary form of a given Kyrgyz term. Generally the nominative
     masculine singular, but may occasionally be another form if the nominative
	 masculine singular is missing.
]=]

local lang = require("Module:languages").getByCode("ky")
local m_table = require("Module:table")
local m_string_utilities = require("Module:string utilities")
local m_script_utilities = require("Module:script utilities")
local iut = require("Module:inflection utilities")
local m_para = require("Module:parameters")

local current_title = mw.title.getCurrentTitle()
local NAMESPACE = current_title.nsText
local PAGENAME = current_title.text

local u = require("Module:string/char")
local rsplit = m_string_utilities.split
local rfind = m_string_utilities.find
local rmatch = m_string_utilities.match
local rgmatch = m_string_utilities.gmatch
local rsubn = m_string_utilities.gsub
local ulen = m_string_utilities.len
local usub = m_string_utilities.sub
local uupper = m_string_utilities.upper
local ulower = m_string_utilities.lower
local insert = table.insert
local dump = mw.dumpObject

-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
	local retval = rsubn(term, foo, bar)
	return retval
end

-- version of rsubn() that returns a 2nd argument boolean indicating whether
-- a substitution was made.
local function rsubb(term, foo, bar)
	local retval, nsubs = rsubn(term, foo, bar)
	return retval, nsubs > 0
end


local lower_vowel = "аоөүуыэяёюие"
local upper_vowel = uupper(lower_vowel)
local vowel = lower_vowel .. upper_vowel
local V = ""
local C = ""

local lower_jr = "йр"
local upper_jr = uupper(lower_jr)
local jr = lower_jr .. upper_jr
local lower_voiced_cons_not_jr = "дмлнңбвгжз"
local upper_voiced_cons_not_jr = uupper(lower_voiced_cons_not_jr)
local voiced_cons_not_jr = lower_voiced_cons_not_jr .. upper_voiced_cons_not_jr
local lower_unvoiced_cons = "кпстфхчцшщ"
local upper_unvoiced_cons = uupper(lower_unvoiced_cons)
local unvoiced_cons = lower_unvoiced_cons .. upper_unvoiced_cons

local letter_classes = {
	-- Subclasses of vowels. These are named after the predominant vowel of the associated suffix.
	a_type_vowel = "аыяюу", -- low or high back vowels
	e_type_vowel = "эие", -- front unrounded vowels
	o_type_vowel = "оё", -- mid back vowels
	oe_type_vowel = "өү", -- front rounded vowels
	y_type_vowel = "аыя", -- back unrounded vowels
	u_type_vowel = "оёюу", -- back rounded vowels
}

local function construct_vowel_harmony_table(spec)
	local tab = {}
	for _, triggers_result in ipairs(spec) do
		local triggers, result = unpack(triggers_result)
		for trigger in rgmatch(triggers, ".") do
			tab = result
		end
	end
	return tab
end

local high_harmony_table = construct_vowel_harmony_table {
	{ "эие", "и" }, -- front unrounded
	{ "аыя", "ы" }, -- back unrounded
	{ "өү", "ү" }, -- front rounded
	{ "оёюу", "у" }, -- back rounded
}
local low_harmony_table = construct_vowel_harmony_table {
	{ "эие", "е" }, -- front unrounded
	{ "аыяюу", "а" }, -- back unrounded
	{ "өү", "ө" }, -- front rounded
	{ "оё", "о" }, -- back rounded
}

local cases = { "nom", "gen", "dat", "acc", "loc", "abl" }
local non_possessed_numbers = { "s", "p" }
local possessed_numbers = { "spos", "mpos" }
local person_number_possession = { "1s", "2s_inform", "2s_formal", "3", "1p", "2p_inform", "2p_formal" }

local person_number_possession_suffixes = {
	 = "им",
	 = "иң",
	 = "иңиз",
	 = "Си",
	 = "ибиз",
	 = "иңар",
	 = "иңиздар",
}

local case_suffixes = {
	 = "",
	 = "Нин",
	 = "га", -- -а after 1s poss and 2s_inform poss, -на after 3 poss
	 = "Ни",
	 = "да", -- -нда after 3 poss
	 = "дан", -- нан after 3 poss
}
local noun_slots = {}

local function make_noun_slots()
	for _, case in ipairs(cases) do
		for _, possessed in ipairs { false, true} do
			if possessed then
				for _, person_number in ipairs(person_number_possession) do
					for _, number in ipairs(possessed_numbers) do
						local slot = ("%s_%s_%s"):format(case, person_number, number)
						accel = slot:gsub("_", "|")
						noun_slots = accel
					end
				end
			else
				for _, number in ipairs(non_possessed_numbers) do
					local slot = ("%s_%s"):format(case, number)
					accel = slot:gsub("_", "|")
					noun_slots = accel
				end
			end
		end
	end
end

make_noun_slots()


local function skip_slot(decl_spec, slot)
	return decl_spec.number == "sg" and (slot:find("_p$") or slot:find("_mpos$")) or
		decl_spec.number == "pl" and (slot:find("_s$") or slot:find("_spos$"))
end


local function combine_stem_ending(stem, ending)
	if ending == "" then
		return stem
	end

	local split_suffix = rsplit(ending, "(" .. V  .. ")")
	local suffix_syls = {}
	for i, sufpart in ipairs(split_suffix) do
		if i == #split_suffix and suffix_syls or i % 2 == 0 then
			suffix_syls = suffix_syls .. sufpart
		else
			insert(suffix_syls, sufpart)
		end
	end

	for _, endsyl in ipairs(suffix_syls) do
		local stem_init, stem_last_vowel, stem_final_cons_cluster = rmatch(stem, "^(.*)(" .. V .. ")(.-)$")
		if not stem_last_vowel then
			error(("Lemma or stem '%s' has no vowel, not sure how to implement vowel harmony"):format(stem))
		end
		local first_endsyl_letter = usub(endsyl, 1)
		local endsyl_init, endsyl_vowel, endsyl_final = rmatch(endsyl, "^(.-)(" .. V .. ")(.*)$")
		if not endsyl_init then
			error(("Internal error: Ending '%s' has no vowel"):format(endsyl))
		end
		if endsyl_vowel ~= "и" and endsyl_vowel ~= "а" then
			error(("Internal error: All endsyl vowels should be high и or low а, but saw %s"):format(endsyl))
		end
		local harmony_table
		if endsyl_vowel == "и" then
			harmony_table = high_harmony_table
		else
			harmony_table = low_harmony_table
		end
		if endsyl_init == "" and stem_final_cons_cluster == "" then
			endsyl_vowel = ""
		else
			endsyl_vowel = harmony_table
		end
		if endsyl_init == "Л" then
			if stem_final_cons_cluster == "" or rfind(stem_final_cons_cluster, "$") then
				endsyl_init = "л"
			else
				endsyl_init = "д"
			end
		elseif endsyl_init == "Н" then
			if stem_final_cons_cluster == "" then
				endsyl_init = "н"
			else
				endsyl_init = "д"
			end
		elseif endsyl_init == "С" then
			if stem_final_cons_cluster == "" then
				endsyl_init = "с"
			else
				endsyl_init = ""
			end
		end
		if rfind(stem_final_cons_cluster, "$") then
			if endsyl_init == "д" then
				endsyl_init = "т"
			elseif endsyl_init == "г" then
				endsyl_init = "к"
			end
		end
		if endsyl_init == "" then
			stem_final_cons_cluster = rsub(stem_final_cons_cluster, "$", {
				 = "б",
				 = "Б",
				 = "г",
				 = "Г",
			})
		end
		stem = stem_init .. stem_last_vowel .. stem_final_cons_cluster .. endsyl_init .. endsyl_vowel .. endsyl_final
	end

	return stem
end


local function decline_noun(decl_spec, lemma)
	local function make_form(stem, number, possession, case)
		local number_ending, possession_ending, case_ending
		if number == "s" then
			number_ending = ""
		else
			number_ending = "Лар"
		end
		local number_stem = number == "p" and decl_spec.pl or combine_stem_ending(stem, number_ending)
		possession_ending = possession and person_number_possession_suffixes or ""
		case_ending = case_suffixes
		if case == "dat" then
			if possession == "1s" or possession == "2s_inform" then
				case_ending = "а"
			elseif possession == "3" then
				case_ending = "на"
			end
		elseif case == "loc" and possession == "3" then
			case_ending = "нда"
		elseif case == "abl" and possession == "3" then
			case_ending = "нан"
		end
		return combine_stem_ending(combine_stem_ending(number_stem, possession_ending), case_ending)
	end

	local function insert_form(slot, form)
		if skip_slot(decl_spec, slot) then
			return
		end
		iut.insert_form(decl_spec.forms, slot, {form = form})
	end

	for _, case in ipairs(cases) do
		for _, possessed in ipairs { false, true} do
			if possessed then
				for _, person_number in ipairs(person_number_possession) do
					for _, number in ipairs(possessed_numbers) do
						local slot = ("%s_%s_%s"):format(case, person_number, number)
						local form = make_form(lemma, number == "mpos" and "p" or "s", person_number, case)
						insert_form(slot, form)
					end
				end
			else
				for _, number in ipairs(non_possessed_numbers) do
					local slot = ("%s_%s"):format(case, number)
					local form = make_form(lemma, number, "", case)
					insert_form(slot, form)
				end
			end
		end
	end

	-- 1. There are two types of stem vowel harmony, based on the last stem vowel: a-e-o-oe (which always occurs when
	--    the first suffix vowel is low а/е/о/ө) vs. y-e-u-oe (which always occurs when the first suffix vowel is high
	--    ы/и/у/ү).
	-- 2. Endings can vary depending on the last sound of the stem in three possible ways:
	--    (a) vowel_or_jr vs. voiced_cons_not_jr vs. unvoiced_cons;
	--    (b) vowel vs. voiced_cons vs. unvoiced_cons;
	--    (c) consonant vs. vowel.
	-- 3. The following consonant suffix variations are found:
	--    (a) Ending variation 2(a) is only associated with the plural suffix -лар, where л after vowel_or_jr becomes д
	--        after voiced_cons_not_jr and т after unvoiced_cons.
	--    (b) Ending variation 2(b) is associated with endings in н- after a vowel, changing to д after a voiced_cons
	--        and т after an unvoiced_cons; or д or г after either vowel or voiced_cons, changing to the corresponding
	--        unvoiced consonant т or к after an unvoiced_cons.
	--    (c) Ending variation 2(c) is only associated with endings beginning with a high-harmonizing suffix vowel
	--        ы/и/у/ү, which disappears after a vowel-final stem.
	-- 4. The following types of vowel suffix variations are found:
	--    (a) low-harmonizing а/е/о/ө;
	--    (b) high-harmonizing ы/и/у/ү;
	--    (c) partial low-harmonizing а/е/а/ө (after у/ю).
	-- Based on this, we use capital letters in suffixes to indicate varying consonant sounds. We use а for the
	-- low-harmonizing vowel and и for the high-harmonizing vowel. Specifically:
	-- * Л: л/д variation.
	-- * Н: н/д variation.
	-- * С: с/- variation.
	-- We automatically handle devoicing of consonants after unvoiced consonants.
	--
	-- We can analyze the suffixes further:
	-- 1. nom_s has no ending.
	-- 2. gen_s uses -нин after a vowel, -дин after a consonant with voicing assimilation.
	-- 3. dat_s uses -га with voicing assimilation. This drops to -а after 1s possessive -им, 2s informal possessive
	--    -иң) and changes to -на after 3 possessive -(с)и.
	-- 4. acc_s uses -ни after a vowel, -ди after a consonant with voicing assimilation.
	-- 5. loc_s uses -да with voicing assimilation. This changes to -нда after 3 possessive -(с)и.
	-- 6. abl_s uses -дан with voicing assimilation. This changes to -нан after 3 possessive -(с)и.
	-- 7. plural uses -лар after a vowel or й/р, -дар after a consonant with voicing assimilation.
	-- 8. 1s possessive uses -им after a consonant, dropping to -м after a vowel.
	-- 9. 2s informal possessive uses -иң after a consonant, dropping to -ң after a vowel.
	-- 10. 2s formal possessive uses -иңиз after a consonant, dropping to -ңиз after a vowel.
	-- 11. 3s/3p possessive uses -и after a consonant, -си after a vowel.
	-- 12. 1p uses -ибиз after a consonant, dropping to -биз after a vowel.
	-- 13. 2p informal possessive uses -иңар after a consonant, dropping to -ңар after a vowel.
	-- 14. 2p formal possessive uses -иңиздар after a consonant, dropping to -ңиздар after a vowel.
end


-- Compute the categories to add the noun to, as well as the annotation to display in the
-- declension title bar. We combine the code to do these functions as both categories and
-- title bar contain similar information.
local function compute_categories_and_annotation(decl_spec)
	local cats = {}
	local function insert(cattype)
		m_table.insertIfNot(cats, "Kyrgyz " .. cattype)
	end
	if decl_spec.number == "sg" then
		insert("uncountable nouns")
	elseif decl_spec.number == "pl" then
		insert("pluralia tantum")
	end
	decl_spec.annotation =
		decl_spec.number == "sg" and "sg-only" or
		decl_spec.number == "pl" and "pl-only" or
		""
	decl_spec.categories = cats
end


local function show_forms(decl_spec)
	local lemmas = {}
	if decl_spec.forms.nom_s then
		for _, nom_s in ipairs(decl_spec.forms.nom_s) do
			table.insert(lemmas, nom_s.form)
		end
	elseif decl_spec.forms.nom_p then
		for _, nom_p in ipairs(decl_spec.forms.nom_p) do
			table.insert(lemmas, nom_p.form)
		end
	end
	local props = {
		lemmas = lemmas,
		slot_table = noun_slots,
		lang = lang,
		include_translit = true,
	}
	iut.show_forms(decl_spec.forms, props)
end


local function make_table(decl_spec)
	local forms = decl_spec.forms

	local header = mw.getCurrentFrame():expandTemplate{
		title = 'inflection-table-top',
		args = {
			title = '{title}{annotation}',
			palette = 'blue',
			tall = 'yes',
			class = 'tr-alongside', -- hack to suppress excess space below each term
		}
	}

	local table_spec_sg = [=[
! class="outer" colspan="9"| singular<br />{jekelik}
|-
!
! —
! first-person<br />singular<br />{menin}
! second-person<br />singular informal<br />{senin}
! second-person<br />singular formal<br />{sizdin}
! third-person<br />singular/plural<br />{anyn_alardyn}
! first-person<br />plural<br />{bizdin}
! second-person<br />plural informal<br />{silerdin}
! second-person<br />plural formal<br />{sizderdin}
|-
! nominative {atooch}
| {nom_s}
| {nom_1s_spos}
| {nom_2s_inform_spos}
| {nom_2s_formal_spos}
| {nom_3_spos}
| {nom_1p_spos}
| {nom_2p_inform_spos}
| {nom_2p_formal_spos}
|-
! genitive {ilik}
| {gen_s}
| {gen_1s_spos}
| {gen_2s_inform_spos}
| {gen_2s_formal_spos}
| {gen_3_spos}
| {gen_1p_spos}
| {gen_2p_inform_spos}
| {gen_2p_formal_spos}
|-
! dative {barysh}
| {dat_s}
| {dat_1s_spos}
| {dat_2s_inform_spos}
| {dat_2s_formal_spos}
| {dat_3_spos}
| {dat_1p_spos}
| {dat_2p_inform_spos}
| {dat_2p_formal_spos}
|-
! accusative {tabysh}
| {acc_s}
| {acc_1s_spos}
| {acc_2s_inform_spos}
| {acc_2s_formal_spos}
| {acc_3_spos}
| {acc_1p_spos}
| {acc_2p_inform_spos}
| {acc_2p_formal_spos}
|-
! locative {jatysh}
| {loc_s}
| {loc_1s_spos}
| {loc_2s_inform_spos}
| {loc_2s_formal_spos}
| {loc_3_spos}
| {loc_1p_spos}
| {loc_2p_inform_spos}
| {loc_2p_formal_spos}
|-
! ablative {chygysh}
| {abl_s}
| {abl_1s_spos}
| {abl_2s_inform_spos}
| {abl_2s_formal_spos}
| {abl_3_spos}
| {abl_1p_spos}
| {abl_2p_inform_spos}
| {abl_2p_formal_spos}
]=]

	local table_spec_pl = [=[
! class="outer" colspan="9" | plural<br />{koeptoegoen}
|-
!
! —
! first-person<br />singular<br />{menin}
! second-person<br />singular informal<br />{senin}
! second-person<br />singular formal<br />{sizdin}
! third-person<br />singular/plural<br />{anyn_alardyn}
! first-person<br />plural<br />{bizdin}
! second-person<br />plural informal<br />{silerdin}
! second-person<br />plural formal<br />{sizderdin}
|-
! nominative {atooch}
| {nom_p}
| {nom_1s_mpos}
| {nom_2s_inform_mpos}
| {nom_2s_formal_mpos}
| {nom_3_mpos}
| {nom_1p_mpos}
| {nom_2p_inform_mpos}
| {nom_2p_formal_mpos}
|-
! genitive {ilik}
| {gen_p}
| {gen_1s_mpos}
| {gen_2s_inform_mpos}
| {gen_2s_formal_mpos}
| {gen_3_mpos}
| {gen_1p_mpos}
| {gen_2p_inform_mpos}
| {gen_2p_formal_mpos}
|-
! dative {barysh}
| {dat_p}
| {dat_1s_mpos}
| {dat_2s_inform_mpos}
| {dat_2s_formal_mpos}
| {dat_3_mpos}
| {dat_1p_mpos}
| {dat_2p_inform_mpos}
| {dat_2p_formal_mpos}
|-
! accusative {tabysh}
| {acc_p}
| {acc_1s_mpos}
| {acc_2s_inform_mpos}
| {acc_2s_formal_mpos}
| {acc_3_mpos}
| {acc_1p_mpos}
| {acc_2p_inform_mpos}
| {acc_2p_formal_mpos}
|-
! locative {jatysh}
| {loc_p}
| {loc_1s_mpos}
| {loc_2s_inform_mpos}
| {loc_2s_formal_mpos}
| {loc_3_mpos}
| {loc_1p_mpos}
| {loc_2p_inform_mpos}
| {loc_2p_formal_mpos}
|-
! ablative {chygysh}
| {abl_p}
| {abl_1s_mpos}
| {abl_2s_inform_mpos}
| {abl_2s_formal_mpos}
| {abl_3_mpos}
| {abl_1p_mpos}
| {abl_2p_inform_mpos}
| {abl_2p_formal_mpos}
]=]

	local footer = mw.getCurrentFrame():expandTemplate{ title = 'inflection-table-bottom' }

	if decl_spec.title then
		forms.title = decl_spec.title
	else
		forms.title = 'Declension of <i lang="ky" class="Cyrl">' .. forms.lemma .. '</i>'
	end

	local function make_text_smaller(text)
		return "(<span style=\"font-size: smaller;\">" .. text .. "</span>)"
	end

	local annotation = decl_spec.annotation
	if annotation == "" then
		forms.annotation = ""
	else
		forms.annotation = " " .. make_text_smaller(annotation)
	end

	local function tag_text(text)
		return make_text_smaller(m_script_utilities.tag_text(text, lang))
	end

	-- grammatical terms used in the table
	forms.jekelik = tag_text("жекелик")
	forms.koeptoegoen = tag_text("көптөгөн")
	forms.atooch = tag_text("атооч")
	forms.ilik = tag_text("илик")
	forms.barysh = tag_text("барыш")
	forms.tabysh = tag_text("табыш")
	forms.jatysh = tag_text("жатыш")
	forms.chygysh = tag_text("чыгыш")
	forms.menin = tag_text("менин")
	forms.senin = tag_text("сенин")
	forms.sizdin = tag_text("сиздин")
	forms.anyn_alardyn = tag_text("анын/алардын")
	forms.bizdin = tag_text("биздин")
	forms.silerdin = tag_text("силердин")
	forms.sizderdin = tag_text("сиздердин")

	local table_spec =
		decl_spec.number == "sg" and table_spec_sg or
		decl_spec.number == "pl" and table_spec_pl or
		table_spec_sg .. "|-\n" .. table_spec_pl
	return m_string_utilities.format(header .. table_spec .. footer, forms)
end


-- Externally callable function to parse and decline a noun where all forms
-- are given manually. Return value is WORD_SPEC, an object where the declined
-- forms are in `WORD_SPEC.forms` for each slot. If there are no values for a
-- slot, the slot key will be missing. The value for a given slot is a list of
-- objects {form=FORM, footnotes=FOOTNOTES}.
function export.do_generate_forms(parent_args, number)
	if number ~= "sg" and number ~= "pl" and number ~= "both" then
		error("Internal error: number (arg 1) must be 'sg', 'pl' or 'both': '" .. number .. "'")
	end

	local params = {
		 = {},
		pl = {}, -- override the plural, for бала pl. балдар instead of expected балалар
		title = {},
	}

	local args = m_para.process(parent_args, params)
	local decl_spec = {
		pl = args.pl,
		title = args.title,
		forms = {},
		number = number,
	}
	local lemma = args or PAGENAME
	if number == "pl" then
		local sg_lemma = rmatch(lemma, "(.*)р$")
		if not sg_lemma then
			error("Plural lemma doesn't end with nominative plural ending (-лар, -дер, -тор, etc.): " .. lemma)
		end
		lemma = sg_lemma
	end
	decline_noun(decl_spec, lemma)
	compute_categories_and_annotation(decl_spec)
	return decl_spec
end


-- Entry point for {{ky-decl-noun}}, {{ky-decl-noun-sg}} and {{ky-decl-noun-pl}}.
function export.show(frame)
	local iparams = {
		 = {required = true},
	}
	local iargs = m_para.process(frame.args, iparams)
	local parent_args = frame:getParent().args
	local decl_spec = export.do_generate_forms(parent_args, iargs)
	show_forms(decl_spec)
	return make_table(decl_spec) .. require("Module:utilities").format_categories(decl_spec.categories, lang)
end

return export