Module:sa-decl/data

From Wiktionary, the free dictionary
Jump to navigation Jump to search
This module needs documentation.
Please document this module by describing its purpose and usage on the documentation page.

local decl_data = {}

local sa_utils = require("Module:sa-utilities")
local SLP_to_IAST = require("Module:sa-utilities/translit/SLP1-to-IAST")
local IAST_to_SLP = require("Module:sa-utilities/translit/IAST-to-SLP1")

local match = mw.ustring.match

-- Make a detection function for ARGS that fetches the stem according to MATCH_RE (which is matched against
-- args.lemma and should have the stem in the first capture). ADDL_CONDITION is an optional function of one
-- argument (ARGS) that must return true for the detection to happen.
local function make_detect(match_re, addl_condition)
	return function(args)
		if addl_condition and not addl_condition(args) then
			return false
		end
		local stem = match(args.lemma, match_re)
		if stem then
			args.stem = stem
			return true
		else
			return false
		end
	end
end

-- Construct all or part of a given noun's declension. Each of SG, DU and PL is a table, whose keys are
-- as follows:
--
-- n = nominative
-- a = accusative
-- v = vocative
-- i = instrumental
-- d = dative
-- ab = ablative
-- g = genitive
-- l = locative
--
-- The corresponding value is one of the following:
-- 1. a "copy spec" such as "[ins]", meaning to copy from the instrumental of the same number;
-- 2. a single string (specifying an ending); or
-- 3. a list of specs, where a spec is either a string (an ending) or a table of the form
--    {"ENDING", stem = "STEM", mono = TRUE/FALSE, note = "NOTE"}. The latter format lets you explicitly specify what
--    the stem is, whether the form is monosyllabic, and what the footnote is. All named keys are optional.
--
-- In forms 2 and 3, if the ending begins with +, the stem defaults to args.lemma; otherwise it defaults to args.stem.
local function decline(args, data, sg, du, pl)
	local cases = {n="nom", a="acc", v="voc", i="ins", d="dat", ab="abl", g="gen", l="loc"}
	local function process_number(endings, tag)
		if not endings then
			return
		end
		for case, es in pairs(endings) do
			if type(es) == "string" and es:find("^%[") then
				-- copy from another case; skip and handle later
			else
				if type(es) == "string" then
					es = {es}
				end
				local forms = {}
				for i, e in ipairs(es) do
					local stem, mono, final, note
					if type(e) == "table" then
						stem = e.stem
						mono = e.mono
						final = e.final
						note = e.note
						e = e[1]
					end
					
					-- reduce Vedic forms to zero in case of novedic parameter
					if args.novedic == true and note and (match(note, "[vV]edic$") or match(note, "Br[aā]hma[nṇ]a")) then 
						stem = ""; e = ""; note = ""
					else
						if e:find("^%+") then
							if stem then
								error("Internal error: Can't use + in an ending when stem is explicitly given")
							end
							e = e:gsub("^%+", "")
							stem = args.lemma
						elseif not stem then
							stem = args.stem
						end

						forms[i] = sa_utils.internal_sandhi({
							stem = stem, ending = e, has_accent = args.has_accent, recessive = case == "v", 
							mono = mono, final = final, ambig_final = args.ambig_final, diaspirate = args.diaspirate,
						})
						if note and note~= "" then
							forms["note" .. i] = note
						end
					end
				end
				data.forms[cases[case] .. "_" .. tag] = forms
			end
		end

		-- Now handle cases copied from another.
		for case, es in pairs(endings) do
			if type(es) == "string" and es:find("^%[") then
				-- copy from another case; skip and handle later
				local other_case = es:match("^%[(.*)%]$")
				if not other_case then
					error("Internal error: Unrecognized copy case spec " .. es)
				end
				local other_slot = other_case .. "_" .. tag
				local value = data.forms[other_slot]
				if not value then
					error("Internal error: Slot '" .. other_slot .. "' to copy from is empty")
				end
				local this_slot = cases[case] .. "_" .. tag
				if data.forms[this_slot] then
					error("Internal error: A value already exists for slot '" .. this_slot ..
						"' when copying from '" .. other_slot .. "'")
				end
				data.forms[cases[case] .. "_" .. tag] = value
			end
		end
	end

	process_number(sg, "s")
	process_number(du, "d")
	process_number(pl, "p")
end


decl_data["a"] = {
	detect = make_detect("(.+)a" .. sa_utils.accent .. "?$",
		function(args) return args.g == "m" or args.g == "n" end)
}

setmetatable(decl_data["a"], {
	__call = function(self, args, data)
		local oxy = match(args.lemma, "(" .. sa_utils.accent .. "?)$")

		data.decl_type = "a-stem"

		if args.g == "m" then
			decline(args, data, {
				n="a" .. oxy .. "s",
				a="a" .. oxy .. "m",
				v="a"
			}, {
				n={ "O" .. oxy, { "A" .. oxy, note = "Vedic" } },
				a="[nom]",
				v={ "O", { "A", note = "Vedic" } }
			}, {
				n={"A" .. oxy .. "s", {"A" .. oxy .. "sas", note="Vedic"}},
				a="A" .. oxy .. "n",
				v={"As", {"Asas", note="Vedic"}}
			})
		else
			decline(args, data, {
				n="a" .. oxy .. "m",
				a="[nom]",
				v="a"
			}, {
				n="e" .. oxy,
				a="[nom]",
				v="e"
			}, {
				n={"A" .. oxy .. "ni", {"A" .. oxy, note="Vedic"}},
				a="[nom]",
				v={"Ani", {"A", note="Vedic"}}
			})
		end

		decline(args, data, {
			i="e" .. oxy .. "na",
			d="A" .. oxy .. "ya",
			ab="A" .. oxy .. "t",
			g="a" .. oxy .. "sya",
			l="e" .. oxy
		}, {
			i="A" .. oxy .. "ByAm",
			d="[ins]",
			ab="[ins]",
			g="a" .. oxy .. "yos",
			l="[gen]"
		}, {
			i={ "E" .. oxy .. "s", { "e" .. oxy .. "Bis", note = "Vedic" } },
			d="e" .. oxy .. "Byas", 
			ab="[dat]",
			g="A" .. oxy .. "nAm", 
			l="e" .. oxy .. "zu"
		})
		table.insert(data.categories, "Sanskrit a-stem nouns")
	end
})

decl_data["iu"] = {
	detect = make_detect("(.+)[iu]" .. sa_utils.accent .. "?$")
}

setmetatable(decl_data["iu"], {
	__call = function(self, args, data)
		local vowel, oxy = match(args.lemma, "([iu])(" .. sa_utils.accent .. "?)$")

		data.decl_type = vowel .. "-stem"

		if args.g == "m" then
			if vowel == "i" then
				decline(args, data, {
					d="a" .. oxy .. "ye",
					l={ "O" .. oxy, { "A" .. oxy, note = "Vedic"}}, -- Whitney §336f
				}, {}, {})
			elseif match(args.stem, sa_utils.consonant .. sa_utils.consonant .. "$") then
				-- the dative ending -ve (vs. -ave) does not occur if the stem has two consonants before -u
				decline(args, data, {
					d="a" .. oxy .. "ve",
					l="O" .. oxy,
				}, {}, {})
			else decline(args, data, {
					d={ "a" .. oxy .. "ve", {stem = args.stem .. vowel, "e" .. oxy, note = "Vedic"}},
					l="O" .. oxy,
				}, {}, {})
			end
			if match(args.stem, sa_utils.consonant .. sa_utils.consonant .. "$") then
				decline(args, data, {
					ab={{sa_utils.up_one_grade[vowel .. oxy] .. "s"}},
				}, {}, {})
			else
				decline(args, data, {
					ab={sa_utils.up_one_grade[vowel .. oxy] .. "s", {stem = args.stem .. vowel, "a" .. oxy .. "s", note = "Vedic"}},
				}, {}, {})
			end
			decline(args, data, {
				n="+s",
				a="+m",
				i={ "+nA", {stem = args.stem .. vowel, "A" .. oxy, note = "Vedic" } },
				g="[abl]",
				v=sa_utils.up_one_grade[vowel .. oxy],
			}, {
				n="+" .. vowel,
				a="[nom]",
				i="+ByAm",
				d="[ins]",
				ab="[ins]",
				g={{stem = args.stem .. vowel, "o" .. oxy .. "s"}},
				l="[gen]",
				v="+" .. vowel,
			}, {
				n={{stem = args.stem .. sa_utils.up_one_grade[vowel .. oxy], "as"}},
				a="+" .. vowel .. "n",
				i="+Bis",
				d="+Byas",
				ab="[dat]",
				g=sa_utils.lengthen[vowel] .. "nA" .. (oxy ~= "" and "/" or "") .. "m",
				l="+su",
				v={{stem = args.stem .. sa_utils.up_one_grade[vowel], "as"}},
			})
		elseif args.g == "f" then
			if vowel == "i" then
				decline(args, data, {
					i={{stem = args.stem .. vowel, "A" .. oxy}, {"+i", note = "Vedic"}},  -- Whitney §336c
					d={ "a" .. oxy .. "ye", {stem = args.stem .. vowel, "E" .. oxy, note = "Later Sanskrit" }, { "+i", note = "Vedic" } },
					l={ "O" .. oxy, {stem = args.stem .. vowel, "A" .. oxy .."m", note = "Later Sanskrit" }, { "A" .. oxy, note = "Vedic"} },
				}, {}, {})
			else
				decline(args, data, {
					i={{stem = args.stem .. vowel, "A" .. oxy }},
					d={ "a" .. oxy .. "ve", {stem = args.stem .. vowel, "E" .. oxy, note = "Later Sanskrit" } },
					l={ "O" .. oxy, {stem = args.stem .. vowel, "A" .. oxy .."m", note = "Later Sanskrit" } },
				}, {}, {})
			end
			decline(args, data, {
				n="+s",
				a="+m",
				ab={sa_utils.up_one_grade[vowel .. oxy] .. "s", {stem = args.stem .. vowel, "A" .. oxy .."s", note = "Later Sanskrit"}, 
					{stem = args.stem .. vowel, "E" .. oxy, note = "Brāhmaṇas"}	},
				g="[abl]",
				v=sa_utils.up_one_grade[vowel .. oxy],
			}, {
				n="+" .. vowel,
				a="[nom]",
				i="+ByAm",
				d="[ins]",
				ab="[ins]",
				g={{stem = args.stem .. vowel, "o" .. oxy .. "s"}},
				l="[gen]",
				v="+" .. vowel,
			}, {
				n={{stem = args.stem .. sa_utils.up_one_grade[vowel .. oxy], "as"}},
				a="+" .. vowel .. "s",
				i="+Bis",
				d="+Byas",
				ab="[dat]",
				g=sa_utils.lengthen[vowel] .. "nA" .. (oxy ~= "" and "/" or "") .. "m",
				l="+su",
				v={{stem = args.stem .. sa_utils.up_one_grade[vowel], "as"}},
			})
		else
			if vowel == "i" then
				decline(args, data, {
					d={ "+ne", { "a" .. oxy .. "ye", note = "Vedic" }},
					ab={ "+nas", { "e" .. oxy .. "s", note = "Vedic"}},
					l={ "+ni", {"O" .. oxy, note = "Vedic"}, {"A" .. oxy, note = "Vedic"}}, -- Whitney §336f + §340f
				}, {}, {})
			elseif match(args.stem, sa_utils.consonant .. sa_utils.consonant .. "$") then
				-- the dative ending -ve (vs. -ave) does not occur if the stem has two consonants before -u; similar for abl./gen. -vas (vs. -os)
				decline(args, data, {
					d={ "+ne", { "a" .. oxy .. "ve", note = "Vedic" }},
					ab={ "+nas", { "o" .. oxy .. "s", note = "Vedic"}},
					l={ "+ni", {"O" .. oxy, note = "Vedic"}},
				}, {}, {})
			else decline(args, data, {
					d={ "+ne", { "a" .. oxy .. "ve", note = "Vedic" }, {stem = args.stem .. vowel, "e" .. oxy, note = "Vedic"}},
					ab={ "+nas", { "o" .. oxy .. "s", note = "Vedic"}, {stem = args.stem .. vowel, "a" .. oxy .. "s", note = "Vedic"}},
					l={ "+ni", {"O" .. oxy, note = "Vedic"}},
				}, {}, {})
			end
			decline(args, data, {
				n="+",
				a="[nom]",
				i={ "+nA", {stem = args.stem .. vowel, "A" .. oxy, note = "Vedic" }},
				g="[abl]",
				v={ "+", sa_utils.up_one_grade[vowel .. oxy] },
			}, {
				n="+nI",
				a="[nom]",
				i="+ByAm",
				d="[ins]",
				ab="[ins]",
				g="+nos",
				l="[gen]",
				v="+nI",
			}, {
				n={ { stem = args.stem .. sa_utils.lengthen[vowel .. oxy], "ni" }, { "+", note = "Vedic"}, { "+" .. vowel, note = "Vedic"} },
				a="[nom]",
				i="+Bis",
				d="+Byas",
				ab="[dat]",
				g=sa_utils.lengthen[vowel] .. "nA" .. (oxy ~= "" and "/" or "") .. "m",
				l="+su",
				v={ { stem = args.stem .. sa_utils.lengthen[vowel], "ni" }, { "+", note = "Vedic"}, { "+" .. vowel, note = "Vedic"} },
			})
		end

		table.insert(data.categories, "Sanskrit " .. vowel .. "-stem nouns")
	end
})

decl_data["AIU"] = {
	detect = function(args)
		if make_detect("(.+)[AIU]" .. sa_utils.accent .. "?$")(args) then
			args.true_mono = sa_utils.is_monosyllabic(args.lemma)
			return true
		else
			return false
		end
	end
}

setmetatable(decl_data["AIU"], {
	__call = function(self, args, data)
		local vowel, oxy = match(args.lemma, "([AIU])(" .. sa_utils.accent .. "?)$")

		data.decl_type = SLP_to_IAST.tr(vowel) .. "-stem"

		if not (args.root or args.compound or args.true_mono) then -- derived stem
			if vowel == "A" then
				decline(args, data, {
					n="+",
					i={ "a" .. oxy .. "yA", { "+", note = "Vedic" } },
					d="+yE",
					ab={"+yAs", {"+yE", note = "Brāhmaṇas"}},
					g="[abl]",
					l="+yAm",
					v="e",
				}, {
					n="e" .. oxy,
					g="a" .. oxy .. "yos",
					v="e",
				}, {
					n="+s",
					v="+s",
				})
			else
				if vowel == "I" then
					decline(args,data, {n="+"})
				else
					decline(args,data, {n="+s"})
				end
				decline(args, data, {
					i={{stem = args.stem .. vowel, "A" .. oxy}},
					d={{stem = args.stem .. vowel, "E" .. oxy}},
					ab={{stem = args.stem .. vowel, "A" .. oxy .. "s"}, {stem = args.stem .. vowel, "E" .. oxy, note = "Brāhmaṇas"}},
					g="[abl]",
					l={{stem = args.stem .. vowel, "A" .. oxy .. "m"}},
					v=sa_utils.shorten[vowel],
				}, {
					 n={ "+O", { "+", note = "Vedic" } },
					 g={{stem = args.stem .. vowel, "o" .. oxy .. "s"}},
					 v={ "+O", { "+", note = "Vedic" } },
				}, {
					n={ "+as", { "+s", note = "Vedic" } },
					v={ "+as", { "+s", note = "Vedic" } },
				})
			end
			decline(args, data, {
				a="+m",
			}, {
				a="[nom]",
				i="+ByAm",
				d="[ins]",
				ab="[ins]",
				l="[gen]",
			}, {
				a="+s",
				i="+Bis",
				d="+Byas",
				ab="[dat]",
				g="+nAm",
				l="+su",
			})
		elseif vowel == "A" then
		-- maybe not accurate for some forms of neuter adjectives, but not clear whether those had separate forms at all (Whitney §367b)
			decline(args, data, {
				n="+s",
				a="+m",
				i="+",
				d="e" .. oxy,
				ab="a" .. oxy .. "s",
				g="[abl]",
				l="i" .. oxy,
				v="+s",
			}, {
				n={ "O" .. oxy, { "A" .. oxy, note = "Vedic" } },
				a="[nom]",
				i="+ByAm",
				d="[ins]",
				ab="[ins]",
				g="o" .. oxy .. "s",
				l="[gen]",
				v={ "O", { "A", note = "Vedic" } }
			}, {
				n="+s",
				a={ "+s", { "a" .. oxy .. "s", note = "Perhaps" } },
				i="+Bis",
				d="+Byas",
				ab="[dat]",
				g={ "+" .. sa_utils.lengthen[vowel] .. "nAm", { "+" .. sa_utils.lengthen[vowel] .. "m", note = "Perhaps" } },
				l="+su",
				v="+s",
			})
		elseif args.compound then
			if match(args.stem, sa_utils.consonant .. sa_utils.consonant .. "$") then
				decline(args, data, {
					a={{"+am", mono = true}},
					i={{"+A", mono = true}},
					d={{"+e", mono = true}},
					ab={{"+as", mono = true}},
					l={{"+i", mono = true}},
				}, {
					n={{"+O", mono = true}},
					g={{"+os", mono = true}},
					v={{"+O", mono = true}},
				}, {
					n={{"+as", mono = true}},
					g={ { "+Am", mono = true }, "+nAm" },
					v={{"+as", mono = true}},
				})
			else
				decline(args, data, {
					a={ { "+am", mono = true }, "+am" },
					i={ { "+A", mono = true }, "+A" },
					d={ { "+e", mono = true }, "+e" },
					ab={ { "+as", mono = true }, "+as" },
					-- weird special case
					l={ { "+i", mono = true }, { stem = args.stem .. sa_utils.vowel_to_cons[vowel], "i" .. (oxy ~= "" and "\\" or "") } },
				}, {
					n={ { "+O", mono = true }, "+O" },
					g={ { "+os", mono = true }, "+os" },
					v={ { "+O", mono = true }, "+O" },
				}, {
					n={ { "+as", mono = true }, "+as" },
					g={ { "+Am", mono = true }, "+nAm", "+Am" },
					v={ { "+as", mono = true }, "+as" },
				})
			end
			decline(args, data, {
				n="+s",
				g="[abl]",
				v="+s",
			}, {
				a="[nom]",
				i="+ByAm",
				d="[ins]",
				ab="[ins]",
				l="[gen]",
			}, {
				a="[nom]",
				i="+Bis",
				d="+Byas",
				ab="[dat]",
				l="+su",
			})
		elseif args.true_mono then
			decline(args, data, {
				n="+s",
				a={ { "+am", mono = true}},
				i={ { "+A" .. oxy, mono = true}},
				d={ { "+e" .. oxy, mono = true }, { "+E" .. oxy, mono = true, note = "Later Sanskrit" } },
				ab={ { "+a" .. oxy .. "s", mono = true }, { "+A" .. oxy .. "s", mono = true, note = "Later Sanskrit" },
					{ "+E" .. oxy, mono = true, note = "Brāhmaṇas" } },
				g="[abl]",
				l={ { "+i" .. oxy, mono = true }, { "+A" .. oxy .. "m", mono = true, note = "Later Sanskrit" } },
				v="[nom]",
			}, {
				n={{"+O", mono = true}},
				a="[nom]",
				i={{"+ByA" .. oxy .. "m", mono = true}},
				d="[ins]",
				ab="[ins]",
				g={{"+o" .. oxy .. "s", mono = true}},
				l="[gen]",
				v="[nom]",
			}, {
				n={{"+as", mono = true}},
				a="[nom]",
				i={{"+Bi" .. oxy .. "s", mono = true}},
				d={{"+Bya" .. oxy .. "s", mono = true}},
				ab="[dat]",
				g={ { "+A" .. oxy .. "m", mono = true }, { "+nA" .. oxy .. "m", mono = true, note = "Later Sanskrit" } },
				l={{"+su" .. oxy, mono = true}},
				v="[nom]",
			})
		else -- polysyllabic stems (Whitney §355 ff.)
			decline(args, data, {
				n="+s",
				a="+am",
				i="+A",
				d="+e",
				ab="+as",
				g="[abl]",
				l="+i",
				v=sa_utils.shorten[vowel],
			}, {
				n="+A",
				a="[nom]",
				i="+ByAm",
				d="[ins]",
				ab="[ins]",
				g="+os",
				l="[gen]",
				v="+A",
			}, {
				n="+as",
				a="[nom]",
				i="+Bis",
				d="+Byas",
				ab="[dat]",
				g="+nAm",
				l="+su",
				v="+as",
			})
		end

		table.insert(data.categories, "Sanskrit " .. SLP_to_IAST.tr(vowel) .. "-stem nouns")
	end
})

decl_data["f"] = { -- actually for nouns in ṛ
	detect = make_detect("(.+)f" .. sa_utils.accent .. "?$")
}

setmetatable(decl_data["f"], { -- actually for nouns in ṛ
	__call = function(self, args, data)
		local oxy = match(args.lemma, "(" .. sa_utils.accent .. "?)$")

		data.decl_type = SLP_to_IAST.tr("f") .. "-stem"

		if args.g == "n" then
			decline(args, data, {
				n="f" .. oxy,
				a="[nom]",
				i="+nA",
				d="+ne",
				ab="+nas",
				l="+ni",
				v={ "f", "ar" },
			}, {
				n="+nI",
				g="+nos",
				v="+nI",
			}, {
				n="F" .. oxy .. "ni",
				a="[nom]",
				v="Fni",
			})
		else
            if not args.r_stem_a then
            	error('Please specify the length of the accusative singular vowel with r_stem_a = "a" or "ā".')
            else
            	args.r_stem_a = IAST_to_SLP.tr(args.r_stem_a)
            end
			decline(args, data, {
				n="A" .. oxy,
				a=args.r_stem_a .. oxy .. "ram",
				i="rA" .. oxy,
				d="re" .. oxy,
				ab="u" .. oxy .. "r",
				l="a" .. oxy .. "ri",
				v="ar",
			}, {
				n={ args.r_stem_a .. oxy .. "rO", { args.r_stem_a .. oxy .. "rA", note = "Vedic" } },
				g="ro" .. oxy .. "s",
				v={ args.r_stem_a .. "rO", { args.r_stem_a .. "rA", note = "Vedic" } },
			}, {
				n=args.r_stem_a .. oxy .. "ras",
				a=args.g == "f" and "F" .. oxy .. "s" or "F" .. oxy .. "n",
				v=args.r_stem_a .. "ras",
			})
		end
		decline(args, data, {
			g="[abl]",
		}, {
			a="[nom]",
			i="+ByAm",
			d="[ins]",
			ab="[ins]",
			l="[gen]",
		}, {
			i="+Bis",
			d="+Byas",
			ab="[dat]",
			g={{stem = args.stem .. "F", "nA" .. (oxy ~= "" and "/" or "") .. "m"}},
			l="+su",
		})

		table.insert(data.categories, "Sanskrit " .. SLP_to_IAST.tr("f") .. "-stem nouns")
	end
})

decl_data["[aiu]s"] = {
	detect = make_detect("(.+)[aiu]" .. sa_utils.accent .. "?s$")
}

setmetatable(decl_data["[aiu]s"], {
	__call = function(self, args, data)
		local vowel, oxy = match(args.lemma, "([aiu])(" .. sa_utils.accent .. "?)s$")

		data.decl_type = vowel .. "s-stem"

		if match(args.stem, "[^a/\\]"..sa_utils.accent.."?y$") and vowel == "a" and args.g == "m" then -- comparatives
			decline(args, data, {
				n="An",
				a="AMsam",
				v={"an", { "+", note = "Rigvedic"}},
			}, {
				n={"AMsO", {"AMsA", note = "Vedic"}},
				v={"AMsO", {"AMsA", note = "Vedic"}},
			}, {
				n="AMsas",
				a="+as",
				v="AMsas",
			})
		elseif args.g == "m" or args.g == "f" then
			if vowel == "a" then
				decline(args, data, {
					n="A" .. oxy .. "s",
					a={ "+am", { "A" .. oxy .. "m", note = "Vedic" } },
				}, nil, {
					n={ "+as", { "A" .. oxy .. "s", note = "Vedic" } },
					v={ "+as", { "A" .. oxy .. "s", note = "Vedic" } },
				})
			else
				decline(args, data, {
					n="+",
					a="+am",
				}, nil, {
					n="+as",
					v="+as",
				})
			end

			decline(args, data, {
				v="+",
			}, {
				n={ "+O", { "+A", note = "Vedic" } },
				v={ "+O", { "+A", note = "Vedic" } },
			}, {
				a="[nom]",
			})
		else
			decline(args, data, {
				n="+",
				a="[nom]",
				v="+",
			}, {
				n="+I",
				v="+I",
			}, {
				n={{stem = args.stem .. sa_utils.lengthen[vowel] .. oxy .. "Ms", "i"}},
				v={{stem = args.stem .. sa_utils.lengthen[vowel] .. "Ms", "i"}},
				a="[nom]",
			})
		end

		decline(args, data, {
			i="+A",
			d="+e",
			ab="+as",
			g="[abl]",
			l="+i",
		}, {
			a="[nom]",
			i= {{"+ByAm", final = true }},
			d="[ins]",
			ab="[ins]",
			g="+os",
			l="[gen]",
		}, {
			i= {{"+Bis", final = true }},
			d= {{"+Byas", final = true }},
			ab="[dat]",
			g="+Am",
			l= {{"+su", final = true }},
		})

		table.insert(data.categories, "Sanskrit " .. vowel .. "s-stem nouns")
	end
})

decl_data["an"] = {
	detect = make_detect("(.+)a" .. sa_utils.accent .. "?n$")
}

setmetatable(decl_data["an"], {
	__call = function(self, args, data)
		local oxy = match(args.lemma, "a(" .. sa_utils.accent .. "?)n$")

		data.decl_type = "an-stem"

		if not match(args.stem, sa_utils.consonant .. "[NYRnmyrlv]$") or args.contract then
			decline(args, data, {
				i="nA" .. oxy,
				d="ne" .. oxy,
				ab="na" .. oxy .. "s",
				g="[abl]",
				l={ "ni" .. oxy, "+i", { "+" , note = "Vedic"} }, -- Whitney §425c for Vedic form
			}, {
				g="no" .. oxy .. "s",
			})

			if args.g ~= "m" then
				decline(args, data, nil, {
					n={ "nI" .. oxy, "+I" },
					v={ "nI", "+I" },
				}, {
					a={ "A" .. oxy .. "ni", {"a" .. oxy, note = "Vedic"},  {"A" .. oxy, note = "Vedic"}},
				})
			else
				decline(args, data, nil, {
					n={ "A" .. oxy .. "nO", { "A" .. oxy .. "nA", note = "Vedic" } },
					v={ "AnO", { "AnA", note = "Vedic" } },
				}, {
					a="na" .. oxy .. "s",
				})
			end
			decline(args, data, nil, nil, {
				g="nA" .. oxy .. "m",
			})
		else
			decline(args, data, {
				i="+A",
				d="+e",
				ab="+as",
				g="[abl]",
				l= { "+i", { "+" , note = "Vedic"} }, -- Whitney §425c for Vedic form
			}, {
				g="+os",
			})

			if args.g ~= "m" then
				decline(args, data, nil, {
					n="+I",
					v="+I",
				}, {
					a={ "A" .. oxy .. "ni", {"a" .. oxy, note = "Vedic"},  {"A" .. oxy, note = "Vedic"}},
				})
			else
				decline(args, data, nil, {
					n={ "A" .. oxy .. "nO", { "A" .. oxy .. "nA", note = "Vedic" } },
					v={ "AnO", { "AnA", note = "Vedic" } },
				}, {
					a="+as",
				})
			end
			decline(args, data, nil, nil, {
				g="+Am",
			})
		end

		if args.g ~= "m" then
			decline(args, data, {
				n="a" .. oxy,
				a="[nom]",
				v={ "+", "a" },
			}, nil, {
				n={ "A" .. oxy .. "ni", {"a" .. oxy, note = "Vedic"},  {"A" .. oxy, note = "Vedic"}},
				v={ "Ani", {"a", note = "Vedic"},  {"A", note = "Vedic"}},
			})
		else
			decline(args, data, {
				n="A" .. oxy,
				a="A" .. oxy .. "nam",
				v="+",
			}, nil, {
				n="A" .. oxy .. "nas",
				v="Anas",
			})
		end

		decline(args, data, nil, {
			a="[nom]",
			i="a" .. oxy .. "ByAm",
			d="[ins]",
			ab="[ins]",
			l="[gen]",
		}, {
			i="a" .. oxy .. "Bis",
			d="a" .. oxy .. "Byas",
			ab="[dat]",
			l="a" .. oxy .. "su",
		})

		table.insert(data.categories, "Sanskrit an-stem nouns")
	end
})

decl_data["in"] = {
	detect = make_detect("(.+)i" .. sa_utils.accent .. "?n$")
}

setmetatable(decl_data["in"], {
	__call = function(self, args, data)
		local oxy = match(args.lemma, "i(" .. sa_utils.accent .. "?)n$")

		data.decl_type = "in-stem"

		if args.g ~= "n" then
			decline(args, data, {
				n="I" .. oxy,
				a="i" .. oxy .. "nam",
				v="+",
			}, {
				n={ "+O", { "+A", note = "Vedic" } },
				v={ "+O", { "+A", note = "Vedic" } },
			}, {
				n="+as",
				a="[nom]",
				v="+as",
			})
		else
			decline(args, data, {
				n="i" .. oxy,
				a="[nom]",
				v={ "i", "+" },
			}, {
				n="+I",
				v="+I",
			}, {
				n="I" .. oxy .. "ni",
				a="[nom]",
				v="Ini",
			})
		end

		decline(args, data, {
			i="+A",
			d="+e",
			ab="+as",
			g="[abl]",
			l="+i",
		}, {
			a="[nom]",
			i="i" .. oxy .. "ByAm",
			d="[ins]",
			ab="[ins]",
			g="+os",
			l="[gen]",
		}, {
			i="i" .. oxy .. "Bis",
			d="i" .. oxy .. "Byas",
			ab="[dat]",
			g="+Am",
			l="i" .. oxy .. "zu",
		})

		table.insert(data.categories, "Sanskrit in-stem nouns")
	end
})

decl_data["root"] = {			-- for root nouns ending on -gh/c/j/th/d/dh/p/bh/m/r/ṣ/ś/h + -ās/os
	detect = function(args)
		-- capture of vowel needed for accent
		if make_detect("(.*)" .. sa_utils.vowel .. sa_utils.accent .. "?r?[GcjTdDpBmrzSh]$")(args) 
			or make_detect("(.*)[Ao]" .. sa_utils.accent .. "?s$")(args)  then
			args.true_mono = sa_utils.is_monosyllabic(args.lemma)
			return true
		else
			return false
		end
	end
}

setmetatable(decl_data["root"], {
	__call = function(self, args, data)
		local vowel, oxy, extra, cons = match(args.lemma, "(" .. sa_utils.vowel .. ")(" .. sa_utils.accent .. "?)(r?)([GcjTdDpBmrzShs])$")
		if match(cons, "[jSh]") ~= nil and not args.ambig_final then
			error('Please specify the final consonant in the nominative singular with ambig_final = "k" or "ṭ".')
		elseif args.ambig_final then -- also for cases like sarágh > saráṭ or potentially anuṣṭúbh > anuṣṭúk
			args.ambig_final = IAST_to_SLP.tr(args.ambig_final)
		end
		local nasal = cons:gsub(".", sa_utils.homorganic_nasal)
		if args.diaspirate then args.diaspirate = true end
		
		data.decl_type = "root-stem"

		-- neuter nom. plural (Whitney §389c, although saying that these forms with infixed nasal are not attested for nouns)
		if args.g == "n" then
			if match(cons, "[mr]") == nil and extra == "" then
				decline(args, data, nil, nil, {
					n= {{stem = args.stem .. vowel .. oxy .. nasal .. cons, "i"}},
					v= {{stem = args.stem .. vowel .. nasal .. cons, "i"}},
				})
			else
				decline(args, data, nil, nil, {
					n= "+i",
					v= "+i",
				})
			end
		end
		
		if args.true_mono then -- monosyllabic stem
			if args.g ~= "n" then
				decline(args, data, {
					-- nom.sg. -s is an 'invisible' (historical) ending to provoke vowel lengthening in -ir/-ur stems, see Whitney §392
					-- this ending should be removed by internal_sandhi at an early stage
					n= {{"+s", mono = true }},  
					a="+am",
					v= {{"+s", mono = true }},
				}, {
					n={ "+O", { "+A", note = "Vedic" } },
					v={ "+O", { "+A", note = "Vedic" } },
				}, {
					n="+as",
					v="+as"
				})
			else
				decline(args, data, {
					n= "+",
					a="[nom]",
					v= "+"
				}, {
					n= {{"+I" .. oxy, mono = true }},
					v= "+I"
				}, { })
			end
			decline(args, data, {
				i= {{ "+A" .. oxy, mono = true }},
				d= {{ "+e" .. oxy, mono = true }},
				ab= {{ "+a" .. oxy .. "s", mono = true }},
				g="[abl]",
				l= {{ "+i" .. oxy, mono = true }},
			}, {
				a= "[nom]",
				i= {{ "+ByA" .. oxy .. "m", mono = true, final = true }},
				d="[ins]",
				ab="[ins]",
				g= {{ "+o" .. oxy .. "s", mono = true }},
				l="[gen]",
			}, {
				a= "[nom]",
				i= {{ "+Bi" .. oxy .. "s", mono = true, final = true }},
				d= {{ "+Bya" .. oxy .. "s", mono = true, final = true }},
				ab="[dat]",
				g= {{ "+A" .. oxy .. "m", mono = true }},
				l= {{ "+su" .. oxy, mono = true , final = true }},
			})
		
		else -- polysyllabic stem, no accent on ending		
			if args.g ~= "n" then
				decline(args, data, {
					n= "+",  
					a="+am",
					v= "+",
				}, {
					n={ "+O", { "+A", note = "Vedic" } },
					v={ "+O", { "+A", note = "Vedic" } },
				}, {
					n="+as",
					v="+as"
				})
			else
				decline(args, data, {
					n= "+",
					a="[nom]",
					v= "+"
				}, {
					n= "+I",
					v= "+I",
				}, { })
			end
			decline(args, data, {
				i="+A",
				d="+e",
				ab="+as",
				g="[abl]",
				l="+i",
			}, {
				a= "[nom]",
				i= {{ "+ByAm", final = true }},
				d="[ins]",
				ab="[ins]",
				g="+os",
				l="[gen]",
			}, {
				a= "[nom]",
				i= {{ "+Bis", final = true }},
				d= {{ "+Byas", final = true }},
				ab="[dat]",
				g= "+Am",
				l= {{ "+su", final = true }},
			})
		end
		
		table.insert(data.categories, "Sanskrit root-stem nouns")
	end
})

decl_data["[Aiuf]t"] = {
	detect = make_detect("(.+)[Aiuf]" .. sa_utils.accent .. "?t$")
}

setmetatable(decl_data["[Aiuf]t"], {
	__call = function(self, args, data)
		local vowel, oxy = match(args.lemma, "([Aiuf])(" .. sa_utils.accent .. "?)t$")
		
		data.decl_type = SLP_to_IAST.tr(vowel) .. "t-stem"

		if args.g ~= "n" then
			decline(args, data, {
				n= vowel .. oxy .. "t",
				a="+am",
				v= vowel .. "t",
			}, {
				n={ "+O", { "+A", note = "Vedic" } },
				v={ "+O", { "+A", note = "Vedic" } },
			}, {
				n="+as",
				a="[nom]",
				v="+as",
			})
		else
			decline(args, data, {
				n= vowel .. oxy .. "t",
				a="[nom]",
				v= vowel .. "t",
			}, {
				n="+I",
				v="+I",
			}, {
				n= vowel .. oxy .. "nti",
				a="[nom]",
				v= vowel .. "nti",
			})
		end

		decline(args, data, {
			i="+A",
			d="+e",
			ab="+as",
			g="[abl]",
			l="+i",
		}, {
			a="[nom]",
			i="+ByAm",
			d="[ins]",
			ab="[ins]",
			g="+os",
			l="[gen]",
		}, {
			i="+Bis",
			d="+Byas",
			ab="[dat]",
			g="+Am",
			l="+su",
		})

		table.insert(data.categories, "Sanskrit " .. SLP_to_IAST.tr(vowel) .. "t-stem nouns")
	end
})

decl_data["at"] = {
	detect = make_detect("(.+)a" .. sa_utils.accent .. "?t$")
}

setmetatable(decl_data["at"], {
	__call = function(self, args, data)
		local cons, oxy = match(args.lemma, "([mv]?)a(" .. sa_utils.accent .. "?)t$")
		
		-- for present participles
		if args.lemma:find("[vm]a/?t$") == nil or args.participle == true then
			data.decl_type = "at-stem"
			
			if args.g ~= "n" then
				decline(args, data, {
					n="a" .. oxy .. "n",
					a="a" .. oxy .. "ntam",
					v="an",
				}, {
					n={ "a" .. oxy .. "ntO", { "a" .. oxy .. "ntA", note = "Vedic"} },
					v={ "antO", { "antA", note = "Vedic"} },
				}, {
					n="a" .. oxy .. "ntas",
					a="ata" .. oxy .. "s",
					v="antas",
				})
			else
				decline(args, data, {
					n="a" .. oxy .. "t",
					a="[nom]",
					v="at",
				}, {
					n="a" .. oxy .. "ntI",
					v="antI",
				}, {
					n="a" .. oxy .. "nti",
					a="[nom]",
					v="anti",
				})
			end
			
			decline(args, data, {
				i="atA" .. oxy,
				d="ate" .. oxy,
				ab="ata" .. oxy .. "s",
				g="[abl]",
				l="ati" .. oxy,
			}, {
				a="[nom]",
				i="a" .. oxy .. "dByAm",
				d="[ins]",
				ab="[ins]",
				g="ato" .. oxy .. "s",
				l="[gen]",
			}, {
				i="a" .. oxy .. "dBis",
				d="a" .. oxy .. "dByas",
				ab="[dat]",
				g="atA" .. oxy .. "m",
				l="+su",
			})

			table.insert(data.categories, "Sanskrit at-stem nouns")
		
		-- for nouns suffixed with -vat/-mat
		else
			data.decl_type = cons .. "at-stem"
			
			if args.g ~= "n" then
				decline(args, data, {
					n="A" .. oxy .. "n",
					a="a" .. oxy .. "ntam",
					v={ "an", { "as", note = "Rigvedic"} },
				}, {
					n={ "a" .. oxy .. "ntO", { "a" .. oxy .. "ntA", note = "Vedic"} },
					v={ "antO", { "antA", note = "Vedic"} },
				}, {
					n="a" .. oxy .. "ntas",
					a="a" .. oxy .. "tas",
					v="antas",
				})
			else
				decline(args, data, {
					n="a" .. oxy .. "t",
					a="[nom]",
					v="at",
				}, {
					n="+I",
					v="+I",
				}, {
					n="a" .. oxy .. "nti",
					a="[nom]",
					v="anti",
				})
			end

			decline(args, data, {
				i="+A",
				d="+e",
				ab="+as",
				g="[abl]",
				l="+i",
			}, {
				a="[nom]",
				i="a" .. oxy .. "dByAm",
				d="[ins]",
				ab="[ins]",
				g="+os",
				l="[gen]",
			}, {
				i="a" .. oxy .. "dBis",
				d="a" .. oxy .. "dByas",
				ab="[dat]",
				g="+Am",
				l="+su",
			})

			table.insert(data.categories, "Sanskrit " .. cons .. "at-stem nouns")
		end
	end
})

decl_data["añc"] = {
	detect = make_detect("(.+[aA]" .. sa_utils.accent .. "?Yc)$",
		function(args) return args.g == "m" or args.g == "n" end)
}

setmetatable(decl_data["añc"], {
	__call = function(self, args, data)
		local cons, vowel, oxy = match(args.lemma, "([yv]?)([aA])(" .. sa_utils.accent .. "?)Yc$")
		
		data.decl_type = "añc-stem"
		
		-- make stem for 'middle' cases
		args.stem = args.stem:gsub("Yc$", "c")
		
		if args.g == "m" then
            decline(args, data, {
                n= "+",  -- args.lemma
                a="+am",
                v= "+",
			}, {
                n={ "+O", { "+A", note = "Vedic" } },
                v={ "+O", { "+A", note = "Vedic" } },
            }, {
                n="+as",
                v="+as",
            })
        else -- neuter
            decline(args, data, {
                n= "",  -- args.stem
                v= "",
                a="[nom]"
            }, {}, {
                n="+i",
                v="+i",
                a="[nom]"
            })
        end
		decline(args, data, {}, {
            i={{"ByAm", final = true}},
        }, {
            i={{"Bis", final = true}},
            d={{"Byas", final = true}},
            l={{"su", final = true}},
        })
        
		-- changing stem to weakest form
		if vowel == "A" then
            -- do nothing
        elseif match(args.stem, "tirya/?c$") then -- specifically for tiryañc
            args.stem = args.stem:gsub("ya(/?)c$", "a%1Sc")
        elseif cons ~= "" then
        	args.stem = args.stem:gsub("([yv])a([/\\]?)c$", 
        	function(cons, acc) return (cons == "y" and "I" or "U") .. (acc == "" and "" or "/") .. "c" end)
        elseif match(args.stem, "dac$") then
            args.stem = args.stem:gsub("ac$", "Ic")
        else
            error("Not supported")
        end
		
		if vowel == "A" or oxy == "" then
			if args.g == "m" then
				decline(args, data, nil, nil, {
					a="as",
				})
			else -- neuter
				decline(args, data, nil, {
					n="I",
					v="I",
				})
			end
			decline(args, data, {
				i="A",
				d="e",
				ab="as",
				l="i",
			}, {
				g="os",
			}, {
				g="Am",
			})
		else -- oxytone accent, showing two possible accentuations (Whitney §410)
            if args.g == "m" then
                decline(args, data, nil, nil, { a={"as", {"a" .. oxy .. "s", note = "Rigvedic", mono = true}} })
            else
                decline(args, data, nil, { 
                    n= {"I", {"I" .. oxy, note = "Rigvedic", mono = true}},
                    v= "I",
                })
            end
            decline(args, data, {
                i= {"A", {"A" .. oxy, note = "Rigvedic", mono = true}},
                d= {"e", {"e" .. oxy, note = "Rigvedic", mono = true}},
                ab= {"as", {"a" .. oxy .. "s", note = "Rigvedic", mono = true}},
                l= {"i", {"i" .. oxy, note = "Rigvedic", mono = true}},
            }, {
                g= {"os", {"o" .. oxy .. "s", note = "Rigvedic", mono = true}},
            }, {
                g= {"Am", {"A" .. oxy .. "m", note = "Rigvedic", mono = true}},
            })
        end
		
		decline(args, data, {
            g="[abl]"
        }, {
            a="[nom]",
            d="[ins]",
            ab="[ins]",
            l="[gen]",
        }, {
            ab="[dat]",
        })
        
		-- not adding noun class (for now?) as these are all adjectives
	end
})

decl_data["o"] = {
	detect = make_detect("(.+)o" .. sa_utils.accent .. "?$")
}

setmetatable(decl_data["o"], {
	__call = function(self, args, data)
		local oxy = match(args.lemma, "o(" .. sa_utils.accent .. "?)$")
		data.decl_type = "o-stem"

		if args.g ~= "n" or "f" or "m" then
			decline(args, data, {
				n="O" .. oxy .."s",
				a="A" .. oxy .."m",
				v="Os",
			}, {
				n={ "A" .. oxy .."vO", { "A" .. oxy .."vA", note = "Vedic" } },
				v={ "AvO", { "AvA", note = "Vedic" } },
			}, {
				n="A" .. oxy .."vas",
				a="A" .. oxy .."s",
				v="Avas",
			})
		end
		decline(args, data, {
			i="+A",
			d="+e",
			ab="o" .. oxy .. "s",
			g="[abl]",
			l="+i",
		}, {
			a="[nom]",
			i="+ByAm",
			d="[ins]",
			ab="[ins]",
			g= "+os",
			l="[gen]",
		}, {
			i="+Bis",
			d="+Byas",
			ab="[dat]",
			g="+Am",
			l="+su",
		})
		
		table.insert(data.categories, "Sanskrit o-stem nouns")
	end
})

decl_data["O"] = {
	detect = make_detect("(.+)O" .. sa_utils.accent .. "?$")
}

setmetatable(decl_data["O"], {
	__call = function(self, args, data)
		local oxy = match(args.lemma, "O(" .. sa_utils.accent .. "?)$")
		data.decl_type = "au-stem"

		if args.g ~= "n" or "f" or "m" then
			decline(args, data, {
				n="+s",
				a="+am",
				v="+s",
			}, {
				n={ "+O", { "+A", note = "Vedic" } },
				v={ "+O", { "+A", note = "Vedic" } },
			}, {
				n="+as",
				v="+as"
			})
		end

		decline(args, data, {
			i= {{ "+A" .. oxy, mono = true }},
			d= {{ "+e" .. oxy, mono = true }},
			ab={{ "+a" .. oxy .. "s", mono = true }},
			g="[abl]",
			l= {{ "+i" .. oxy, mono = true }},
		}, {
			a="[nom]",
			i= {{"+ByA" .. oxy .. "m", mono = true }},
			d="[ins]",
			ab="[ins]",
			g= {{ "+o" .. oxy .. "s", mono = true }},
			l="[gen]",
		}, {
			a="[nom]",
			i= {{"+Bi" .. oxy .. "s", mono = true }},
			d= {{"+Bya" .. oxy .. "s", mono = true }},
			ab="[dat]",
			g= {{ "+A" .. oxy .. "m", mono = true }},
			l= {{ "+su" .. oxy, mono = true }},
		})

		table.insert(data.categories, "Sanskrit au-stem nouns")
	end
})

decl_data["vāṃs"] = {
	detect = make_detect("(.*[^i])i?vA" .. sa_utils.accent .. "?Ms$",
		function(args) return args.g == "m" or args.g == "n" end)
}

setmetatable(decl_data["vāṃs"], {
	__call = function(self, args, data)
		local extra_i, oxy = match(args.lemma, "(i?)vA(" .. sa_utils.accent .. "?)Ms$")
		
		data.decl_type = "vāṃs-stem"

		if args.g == "m" then
			decline(args, data, {
				n= extra_i .. "vA" .. oxy .. "n",
				a= extra_i .. "vA" .. oxy .. "Msam",
				v={ extra_i .. "van", { extra_i .. "vas", note = "Rigvedic"} },
			}, {
				n= extra_i .. "vA" .. oxy .. "MsO",
				a="[nom]",
				v= extra_i .. "vAMsO",
			}, {
				n= extra_i .. "vA" .. oxy .. "Msas",
				a="u" .. oxy .. "zas",
				v= extra_i .. "vAMsas",
			})
		else
			decline(args, data, {
				n= extra_i .. "va" .. oxy .. "t",
				a="[nom]",
				v= extra_i .. "vat"
			}, {
				n="u" .. oxy .. "zI",
				a="[nom]",
				v="uzI",
			}, {
				n= extra_i .. "vA" .. oxy .. "MsI",
				a="[nom]",
				v= extra_i .. "vAMsI"
			})
		end

		decline(args, data, {
			i="u" .. oxy .. "zA",
			d="u" .. oxy .. "ze",
			ab="u" .. oxy .. "zas",
			g="u" .. oxy .. "zas",
			l="u" .. oxy .. "zi"
		}, {
			i= extra_i .. "va" .. oxy .. "dByAm",
			d="[ins]",
			ab="[ins]",
			g="u" .. oxy .. "zos",
			l="[gen]"
		}, {
			i= extra_i .. "va" .. oxy .. "dBis",
			d= extra_i .. "va" .. oxy .. "dByas", 
			ab="u" .. oxy .. "zAm",
			g="u" .. oxy .. "zAm", 
			l= extra_i .. "va" .. oxy .. "tsu"
		})
		table.insert(data.categories, "Sanskrit vāṃs-stem nouns")
	end
})

return decl_data