Module:sla-noun/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 declensions = {}

local com = require("Module:sla-common")
local m_table_tools = require("Module:table tools")
local lang = require("Module:languages").getByCode("sla-pro")

local u = mw.ustring.char
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

local GRAVE = u(0x0300) -- grave =  ̀
local TILDE = u(0x0303) -- tilde =  ̃
local INVBREVE = u(0x0311) -- inverse breve =  ̑
local MACRON = u(0x0304) -- macron =  ̄
local UNK = "-" -- unknown accent (placeholder; existing accents removed)
local LEAVE = nil -- leave the accent alone

local contract_across_j_note = "The second form occurs in languages that contract early across /j/ (e.g. Czech), while the first form occurs in languages that do not (e.g. Russian)."
local a_stem_loc_pl_note = "''-asъ'' is the expected Balto-Slavic form but is found only in some Old Czech documents; ''-axъ'' is found everywhere else and is formed by analogy with other locative plurals in ''-xъ''."
local hard_o_stem_ins_sg_note = "''-ъmь'' in North Slavic, ''-omь'' in South Slavic."
local soft_o_stem_ins_sg_note = "''-ьmь'' in North Slavic, ''-emь'' in South Slavic."
local cons_stem_alt_pl_note = "''-ьmъ''/etc. are the original consonant-stem endings, while ''-amъ''/etc. are later Common Slavic endings formed by analogy with a-stems."

local function combine_stem_ending(stem, endinfo)
	if endinfo.override then
		return endinfo.override
	end
	local accent = endinfo[1]
	local ending = endinfo[2]
	if endinfo.pal1 then
		stem = com.first_palatalization(stem)
	elseif endinfo.pal2 then
		stem = com.second_palatalization(stem)
	elseif endinfo.iotate then
		stem = com.iotate(stem)
	end
	if accent then
		stem = com.set_accent(stem, accent)
	end
	return stem .. ending
end

local next_notesym_table = {
	["*"]="**",
	["**"]="***",
	["***"]="†",
	["†"]="††",
	["††"]="†††",
	["†††"]="‡",
	["‡"]="‡‡",
	["‡‡"]="‡‡",
	["‡‡‡"]="1"
}

local function get_next_notesym(sym)
	return next_notesym_table[sym] or sym + 1
end
	
-- Value is either a single string (substitute that exact string),
-- an "ending table" or a list of ending tables. An ending table is
-- a table of one item (a string, substitute that exact string) or
-- two items (the accent to apply to the stem, and the ending).
-- Either form can have optional named values attached. In both
-- forms, 'footnote' can be used to specify a footnote; in the
-- two-item form, 'pal1', 'pal2', and 'iotate' can additionally be
-- given to cause the appropriate modification to the end of the
-- stem.
local function add_form(data, stem, endinfo)
	if type(endinfo) ~= "table" then
		endinfo = {endinfo}
	end
	if type(endinfo[1]) ~= "table" then
		endinfo = {endinfo}
	end
	local retval = {}
	for _, endi in ipairs(endinfo) do
		local form
		if endi[2] then
			form = combine_stem_ending(stem, endi)
		else
			form = endi[1]
		end
		if endi.footnote then
			local notesym = data.footnote_to_sym[endi.footnote]
			if not notesym then
				notesym = data.next_notesym
				data.next_notesym = get_next_notesym(notesym)
				data.footnote_to_sym[endi.footnote] = notesym
				table.insert(data.footnotes, m_table_tools.superscript_notes(notesym) .. " " .. endi.footnote)
			end
			table.insert(retval, {form, notesym=notesym})
		else
			table.insert(retval, form)
		end
	end
	return retval
end
		
-- Add a full set of forms to the forms in FORMS for a given stem and the
-- ending specs for all endings. The value of SG, DU and PL is a table of
-- ending specs, containing entries for individual cases (n=nominative,
-- a=accusative, g=genitive, l=locative, d=dative, i=instrumental, v=vocative).
-- All seven values should be provided for the singular; all but vocative
-- should be provided for the plural (vocative plural is always the same as
-- nominative plural); for the dual, only nominative, genitive and dative
-- should be provided (accusative and vocative are the same as nominative,
-- locative is the same as genitive, and instrumental is the same as dative).
--
-- Each ending spec is either a string (use that exact string for the entire
-- case form) or a table SPEC indicating how to construct the case form, or
-- a list of such table SPECS if there are multiple possibilities. SPEC should
-- be either a list containing a single string (use that exact string for the
-- entire case form), or a list containing two strings and optional additional
-- flags. In the latter case, where SPEC[1] is the accent to place on the
-- stem (or an empty string to remove any stressed accent but leave macrons,
-- or the value UNK [for "unknown"] to remove all accents, or the value
-- LEAVE to leave any existing accents), SPEC[2] is the ending to add to the
-- stem, optional SPEC.pal1 causes the stem to have the first palatalization
-- applied, optional SPEC.pal2 causes the stem to have the second palatalization 
-- applied, optional SPEC.iotate causes the stem to have iotation applied.
local function add_forms(data, forms, stem, sg, du, pl)
	forms.nom = {
		s = add_form(data, stem, sg.n),
		d = add_form(data, stem, du.n),
		p = add_form(data, stem, pl.n),
	}
	forms.acc = {
		s = add_form(data, stem, sg.a),
		d = forms.nom.d,
		p = add_form(data, stem, pl.a),
	}
	forms.gen = {
		s = add_form(data, stem, sg.g),
		d = add_form(data, stem, du.g),
		p = add_form(data, stem, pl.g),
	}
	forms.loc = {
		s = add_form(data, stem, sg.l),
		d = forms.gen.d,
		p = add_form(data, stem, pl.l),
	}
	forms.dat = {
		s = add_form(data, stem, sg.d),
		d = add_form(data, stem, du.d),
		p = add_form(data, stem, pl.d),
	}
	forms.ins = {
		s = add_form(data, stem, sg.i),
		d = forms.dat.d,
		p = add_form(data, stem, pl.i),
	}
	forms.voc = {
		s = add_form(data, stem, sg.v),
		d = forms.nom.d,
		p = forms.nom.p,
	}
end

-- All consonant stems (ū, r, masc/neut n, s, nt) behave very similarly so
-- we merge their functionality. All such stems have an infix composed of
-- a vowel (V) + a consonant (C), which occurs between the stem and ending in
-- all cases but the nominative/vocative (and accusative singular of neuters).
-- DOPAL should be true if the stem should be first-palatalized before the
-- infix, and ALTFEMPL is true if alternative endings in -am* should be given
-- as well (used for v-stems).
local function handle_consonant_stem(data, v, c, g, dopal, altfempl)
	local forms = {}
	local palstem = dopal and com.first_palatalization(data.stem) or data.stem
	local nonsyll = com.is_nonsyllabic(palstem)
	local ap = data.ap
	-- FIXME! Don't know the actual accents of *dьnь (pattern c),
	-- so treat as if unknown.
	if nonsyll and g == "m" then
		ap = nil
	end
	
	if ap == "a" or ap == "b" then
		add_forms(data, forms, palstem .. v .. (data.ap == "b" and GRAVE or "") .. c,
			-- FIXME! Need to check voc, need accents for dual
			-- FIXME! Formerly had ьmi for inst pl of all genders
			{n=data.nom_sg, a=g == "n" and data.nom_sg or {GRAVE, "ь"}, g={GRAVE, "e"}, l={GRAVE, "e"}, d={GRAVE, "i"}, i=g == "f" and {{GRAVE, "ьjǫ"}, {GRAVE, "ǭ", iotate=true, footnote=contract_across_j_note}} or {GRAVE, "ьmь"}, v=data.nom_sg},
			{n={GRAVE, "i"}, g={GRAVE, "u"},
				d=altfempl and {{GRAVE, "ьma"}, {GRAVE, "ama", footnote=cons_stem_alt_pl_note}} or {GRAVE, "ьma"}},
			{n={GRAVE, g == "m" and "e" or g == "f" and "i" or "ā"}, a={GRAVE, g == "n" and "ā" or "i"}, g={GRAVE, "ъ"},
				l=altfempl and {{GRAVE, "ьxъ"}, {GRAVE, "axъ", footnote=cons_stem_alt_pl_note}} or {GRAVE, "ьxъ"},
				d=altfempl and {{GRAVE, "ьmъ"}, {GRAVE, "amъ", footnote=cons_stem_alt_pl_note}} or {GRAVE, "ьmъ"},
				i=altfempl and {{GRAVE, "ьmī"}, {GRAVE, "amī", footnote=cons_stem_alt_pl_note}} or {GRAVE, g == "n" and "ȳ" or "ьmī"}
			}
		)
	elseif ap == "c" then
		local ldac = nonsyll and INVBREVE or TILDE
		add_forms(data, forms, palstem .. v .. c,
			-- FIXME! Need to check voc
			-- FIXME! Formerly had ьmi for inst pl of all genders
			{n=data.nom_sg, a=g == "n" and data.nom_sg or {INVBREVE, "ь"}, g={INVBREVE, "e"}, l={INVBREVE, "e"}, d={INVBREVE, "i"}, i=g == "f" and {"", "ьjǫ́"} or {INVBREVE, "ьmь"}, v=data.nom_sg},
			{n={INVBREVE, "i"}, g={"", "ù"},
				d=altfempl and {{"", "ьmà"}, {"", "àma", footnote=cons_stem_alt_pl_note}} or {"", "ьmà"}},
			{n=g == "n" and {"", "à"} or g == "m" and {INVBREVE, "e"} or {INVBREVE, "i"}, a=g == "n" and {"", "à"} or {INVBREVE, "i"}, g={TILDE, "ъ"},
				l=altfempl and {{ldac, "ьxъ"}, {ldac, "axъ", footnote=cons_stem_alt_pl_note}} or {ldac, "ьxъ"},
				d=altfempl and {{ldac, "ьmъ"}, {ldac, "amъ", footnote=cons_stem_alt_pl_note}} or {ldac, "ьmъ"},
				i=altfempl and {{"", "ьmì"}, {"", "amì", footnote=cons_stem_alt_pl_note}} or {"", g == "n" and "ý" or "ьmì"}
			}
		)
	else
		add_forms(data, forms, palstem .. v .. c,
			-- FIXME! Need to check voc, need accents for dual
			-- FIXME! Formerly had ьmi for inst pl of all genders
			{n=data.nom_sg, a=g == "n" and data.nom_sg or {UNK, "ь"}, g={UNK, "e"}, l={UNK, "e"}, d={UNK, "i"}, i=g == "f" and {{UNK, "ьjǫ"}, {UNK, "ǫ", iotate=true, footnote=contract_across_j_note}} or {UNK, "ьmь"}, v=nonsyll and g == "m" and {UNK, "y"} or data.nom_sg},
			{n={UNK, "i"}, g={UNK, "u"},
				d=altfempl and {{UNK, "ьma"}, {UNK, "ama", footnote=cons_stem_alt_pl_note}} or {UNK, "ьma"}},
			{n={UNK, g == "m" and "e" or g == "f" and "i" or "a"}, a={UNK, g == "n" and "a" or "i"}, g={UNK, "ъ"},
				l=altfempl and {{UNK, "ьxъ"}, {UNK, "axъ", footnote=cons_stem_alt_pl_note}} or {UNK, "ьxъ"},
				d=altfempl and {{UNK, "ьmъ"}, {UNK, "amъ", footnote=cons_stem_alt_pl_note}} or {UNK, "ьmъ"},
				i=altfempl and {{UNK, "ьmi"}, {UNK, "ami", footnote=cons_stem_alt_pl_note}} or {UNK, g == "n" and "y" or "ьmi"}
			}
		)
	end
 
	return forms
end

declensions["v-stem"] = function(data)
	if data.desinence ~= "y" and data.desinence ~= "i" then
		error("u-stem nouns must end in -y or -i")
	end

	return handle_consonant_stem(data, "ъ", "v", "f", false, "altfempl"), "v-stem"
end

declensions["s-stem"] = function(data)
	if data.desinence ~= "e" and data.desinence ~= "o" then
		error("s-stem nouns must end in -e or -o")
	end

	return handle_consonant_stem(data, "e", "s", "n", "palatalize"), "s-stem"
end

declensions["r-stem"] = function(data)
	if (data.unom_sg ~= "mati") and (data.unom_sg ~= "dъťi") then
		error("Must be used only for nouns *mati and *dъťi")
	end

	return handle_consonant_stem(data, "e", "r", "f", "palatalize"), "r-stem"
end

declensions["nt-stem"] = function(data)
	if data.desinence ~= "ę" then
		error("nt-stem nouns must end in -ę")
	end

	return handle_consonant_stem(data, "ę", "t", "n", "palatalize"), "nt-stem"
end

declensions["n-stem"] = function(data)
	local forms

	-- masculine nouns have desinence end in -nь or -y, neuters in -ę
	local y_type = data.desinence == "y"
	local ni_type = data.desinence == "ь"
	local e_type = data.desinence == "ę"

	if not(y_type or ni_type or e_type) then
		error("Wrong ending for an n-stem noun")
	end

	if ni_type then
		if data.ustem ~= "dьn" then
			error("n-stem noun ending in -ь must be *dьnь")
		end
		data.stem = "d"
		data.ustem = "d"
		forms = handle_consonant_stem(data, "", "", "m", "palatalize")
	else
		forms = handle_consonant_stem(data, "e", "n", y_type and "m" or "n", "palatalize")
	end

	return forms, "n-stem", e_type and {"neuter n-stem nouns"} or {"masculine n-stem nouns"}
end

-- Masculine and feminine i-stems differ only in the instrumental singular and
-- nominative plural so handle them together.
local function handle_i_stem(data, masc)
	local forms = {}

	if data.ap == "a" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for voc, dual
			{n=data.nom_sg, a=data.nom_sg, g={GRAVE, "ī"}, l={GRAVE, "ī"}, d={GRAVE, "i"}, i=masc and {GRAVE, "ьmь"} or {{GRAVE, "ьjǫ"}, {GRAVE, "ǭ", iotate=true, footnote=contract_across_j_note}}, v={UNK, "i"}},
			{n={GRAVE, "i"}, g={{GRAVE, "ьju"}, {GRAVE, "u", iotate=true, footnote=contract_across_j_note}}, d={GRAVE, "ьma"}},
			{n=masc and {{GRAVE, "ьjē"}, {GRAVE, "ē", iotate=true, footnote=contract_across_j_note}} or {GRAVE, "i"}, a={GRAVE, "i"}, g={{GRAVE, "ьjь"}, {GRAVE, "ī", footnote=contract_across_j_note}}, l={GRAVE, "ьxъ"}, d={GRAVE, "ьmъ"}, i={GRAVE, "ьmī"}}
		)
	elseif data.ap == "b" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for voc, dual
			{n={TILDE, "ь"}, a={TILDE, "ь"}, g={TILDE, "i"}, l={TILDE, "i"}, d={MACRON, "ì"}, i=masc and {MACRON, "ь̀mь"} or {{TILDE, "ьjǫ"}, {TILDE, "ǫ", iotate=true, footnote=contract_across_j_note}}, v={UNK, "i"}},
			{n={MACRON, "ì"}, g={{TILDE, "ьju"}, {UNK, "u", iotate=true, footnote=contract_across_j_note}}, d={TILDE, "ьma"}},
			{n=masc and {{TILDE, "ьjē"}, {TILDE, "ē", iotate=true, footnote=contract_across_j_note}} or {MACRON, "ì"}, a={MACRON, "ì"}, g={{MACRON, "ь̀jь"}, {TILDE, "i", footnote=contract_across_j_note}}, l={MACRON, "ь̀xъ"}, d={MACRON, "ь̀mъ"}, i={TILDE, "ьmī"}}
		)
	elseif data.ap == "c" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for voc
			{n={INVBREVE, "ь"}, a={INVBREVE, "ь"}, g={"", "í"}, l={"", "í"}, d={INVBREVE, "i"}, i=masc and {INVBREVE, "ьmь"} or {"", "ьjǫ́"}, v={UNK, "i"}},
			{n={INVBREVE, "i"}, g={{"", "ьjù"}, {UNK, "u", iotate=true, footnote=contract_across_j_note}}, d={"", "ьmà"}},
			{n=masc and {{INVBREVE, "ьjē"}, {INVBREVE, "ē", iotate=true, footnote=contract_across_j_note}} or {INVBREVE, "i"}, a={INVBREVE, "i"}, g={"", "ь̀jь"}, l={INVBREVE, "ьxъ"}, d={INVBREVE, "ьmъ"}, i={"", "ьmì"}}
		)
	else
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a=data.nom_sg, g={UNK, "i"}, l={UNK, "i"}, d={UNK, "i"}, i=masc and {UNK, "ьmь"} or {{UNK, "ьjǫ"}, {UNK, "ǫ", iotate=true, footnote=contract_across_j_note}}, v={UNK, "i"}},
			{n={UNK, "i"}, g={{UNK, "ьju"}, {UNK, "u", iotate=true, footnote=contract_across_j_note}}, d={UNK, "ьma"}},
			{n=masc and {{UNK, "ьje"}, {UNK, "e", iotate=true, footnote=contract_across_j_note}} or {UNK, "i"}, a={UNK, "i"}, g={{UNK, "ьjь"}, {UNK, "i", footnote=contract_across_j_note}}, l={UNK, "ьxъ"}, d={UNK, "ьmъ"}, i={UNK, "ьmi"}}
		)
	end

	return forms, "i-stem", masc and {"masculine i-stem nouns"} or {"feminine i-stem nouns"}
end

declensions["feminine i-stem"] = function(data)
	return handle_i_stem(data, false)
end

declensions["masculine i-stem"] = function(data)
	return handle_i_stem(data, "masc")
end

declensions["u-stem"] = function(data)
	local forms = {}

	if data.desinence ~= "ъ" then
		error("u-stem nouns must end in -ъ")
	end
	
	if data.ap == "a" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for voc, dual
			{n=data.nom_sg, a=data.nom_sg, g={GRAVE, "u"}, l={GRAVE, "ū"}, d={GRAVE, "ovi"}, i={GRAVE, "ъmь"}, v={GRAVE, "u"}},
			{n={GRAVE, "y"}, g={GRAVE, "ovu"}, d={GRAVE, "ъma"}},
			{n={GRAVE, "ove"}, a={GRAVE, "y"}, g={GRAVE, "ovъ"}, l={GRAVE, "ъxъ"}, d={GRAVE, "ъmъ"}, i={GRAVE, "ъmī"}}
		)
	elseif data.ap == "b" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for voc
			{n={TILDE, "ъ"}, a={TILDE, "ъ"}, g={MACRON, "ù"}, l={TILDE, "u"}, d={MACRON, "òvi"}, i={MACRON, "ъ̀mь"}, v={UNK, "u"}},
			{n={MACRON, "ỳ"}, g={MACRON, "òvu"}, d={TILDE, "ъma"}},
			{n={MACRON, "òve"}, a={MACRON, "ỳ"}, g={MACRON, "òvъ"}, l={MACRON, "ъ̀xъ"}, d={MACRON, "ъ̀mъ"}, i={TILDE, "ъmī"}}
		)
	elseif data.ap == "c" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for voc
			{n={INVBREVE, "ъ"}, a={INVBREVE, "ъ"}, g={INVBREVE, "u"}, l={"", "ú"}, d={INVBREVE, "ovi"}, i={INVBREVE, "ъmь"}, v={UNK, "u"}},
			{n={INVBREVE, "y"}, g={"", "ovù"}, d={"", "ъmà"}},
			{n={INVBREVE, "ove"}, a={INVBREVE, "y"}, g={"", "òvъ"}, l={INVBREVE, "ъxъ"}, d={INVBREVE, "ъmъ"}, i={"", "ъmì"}}
		)
	else
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a=data.nom_sg, g={UNK, "u"}, l={UNK, "u"}, d={UNK, "ovi"}, i={UNK, "ъmь"}, v={UNK, "u"}},
			{n={UNK, "y"}, g={UNK, "ovu"}, d={UNK, "ъma"}},
			{n={UNK, "ove"}, a={UNK, "y"}, g={UNK, "ovъ"}, l={UNK, "ъxъ"}, d={UNK, "ъmъ"}, i={UNK, "ъmi"}}
		)
	end

	return forms, "u-stem"
end

declensions["hard a-stem"] = function(data)
	local forms = {}

	if data.ap == "a" then
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a={GRAVE, "ǫ"}, g={GRAVE, "y"}, l={GRAVE, "ě", pal2=true}, d={GRAVE, "ě", pal2=true}, i={{GRAVE, "ojǫ"}, {GRAVE, "ǭ", footnote=contract_across_j_note}}, v={GRAVE, "o"}},
			{n={GRAVE, "ě", pal2=true}, g={GRAVE, "u"}, d={GRAVE, "ama"}},
			{n={GRAVE, "y"}, a={GRAVE, "y"}, g={GRAVE, "ъ"}, l={{GRAVE, "asъ"}, {GRAVE, "axъ", footnote=a_stem_loc_pl_note}}, d={GRAVE, "amъ"}, i={GRAVE, "amī"}}
		)
	elseif data.ap == "b" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for voc
			{n={MACRON, "à"}, a={MACRON, "ǫ̀"}, g={MACRON, "ỳ"}, l={MACRON, "ě̀", pal2=true}, d={MACRON, "ě̀", pal2=true}, i={{MACRON, "òjǫ"}, {TILDE, "ǫ", footnote=contract_across_j_note}}, v={UNK, "o"}},
			{n={TILDE, "ě", pal2=true}, g={MACRON, "ù"}, d={MACRON, "àma"}},
			{n={MACRON, "ỳ"}, a={MACRON, "ỳ"}, g={TILDE, "ъ"}, l={{MACRON, "àsъ"}, {MACRON, "àxъ", footnote=a_stem_loc_pl_note}}, d={MACRON, "àmъ"}, i={MACRON, "àmī"}}
		)
	elseif data.ap == "c" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for voc
			{n={"", "à"}, a={INVBREVE, "ǫ"}, g={"", "ý"}, l={INVBREVE, "ě", pal2=true}, d={"", "ě̀", pal2=true}, i={"", "ojǫ́"}, v={UNK, "o"}},
			{n={INVBREVE, "ě", pal2=true}, g={"", "ù"}, d={"", "àma"}},
			{n={INVBREVE, "y"}, a={INVBREVE, "y"}, g={TILDE, "ъ"}, l={{"", "àsъ"}, {"", "àxъ", footnote=a_stem_loc_pl_note}}, d={"", "àmъ"}, i={"", "àmi"}}
		)
	else
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a={UNK, "ǫ"}, g={UNK, "y"}, l={UNK, "ě", pal2=true}, d={UNK, "ě", pal2=true}, i={{UNK, "ojǫ"}, {UNK, "ǫ", footnote=contract_across_j_note}}, v={LEAVE, "o"}},
			{n={UNK, "ě", pal2=true}, g={UNK, "u"}, d={UNK, "ama"}},
			{n={UNK, "y"}, a={UNK, "y"}, g={UNK, "ъ"}, l={{UNK, "asъ"}, {UNK, "axъ", footnote=a_stem_loc_pl_note}}, d={UNK, "amъ"}, i={UNK, "ami"}}
		)
	end

	return forms, "hard a-stem"
end

declensions["soft a-stem"] = function(data)
	local forms = {}
	local stem = data.ustem
	local title
	-- ī-stems share the same endings as soft a-stems in all cases except for the nominative singular, so they are handled as a
	-- special case of them.
	-- for genuine ī-stems that are not preceded by a soft, palatal consonant (e.g. olni) we add "j" everywhere except in nom_sg	
	if data.desinence == "i" then
		title = "ī-stem"
		stem = com.iotate(stem)
	else
		if not rfind(stem, "[cčďjľňřšťž]$") and not rfind(stem, "dz$") then
			error("Soft a-stem must end in a soft consonant (given: " .. stem .. ")")
		end
		title = "soft a-stem"
	end
	
	if data.ap == "a" then
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a={GRAVE, "ǫ"}, g={GRAVE, "ę̇"}, l={GRAVE, "ī"}, d={GRAVE, "ī"}, i={{GRAVE, "ējǫ"}, {GRAVE, "ǭ", footnote=contract_across_j_note}}, v={GRAVE, "e"}},
			{n={GRAVE, "i"}, g={GRAVE, "u"}, d={GRAVE, "ama"}},
			{n={GRAVE, "ę̇"}, a={GRAVE, "ę̇"}, g={GRAVE, "ь"}, l={GRAVE, "āsъ"}, d={GRAVE, "āmъ"}, i={GRAVE, "āmī"}}
		)
	elseif data.ap == "b" then
		-- Some accent-b ja-stem nouns behave more like a-stem nouns (e.g.
		-- *svě̄ťà), while others take the standard form with neoacute accent
		-- (vòlja). Remember that data.nom_sg and data.stem are decomposed.
		if rfind(data.nom_sg, "a" .. GRAVE .. "$") then
			add_forms(data, forms, data.stem,
				-- FIXME! Need accents for voc
				{n={MACRON, "à"}, a={MACRON, "ǫ̀"}, g={MACRON, "ę̇̀"}, l={MACRON, "ì"}, d={MACRON, "ì"}, i={{MACRON, "èjǫ"}, {TILDE, "ǫ", footnote=contract_across_j_note}}, v={UNK, "e"}},
				{n={TILDE, "i"}, g={MACRON, "ù"}, d={MACRON, "àma"}},
				{n={MACRON, "ę̇̀"}, a={MACRON, "ę̇̀"}, g={TILDE, "ь"}, l={{MACRON, "àsъ"}, {MACRON, "àxъ", footnote=a_stem_loc_pl_note}}, d={MACRON, "àmъ"}, i={MACRON, "àmī"}}
			)
		else
			add_forms(data, forms, data.stem,
				-- FIXME! Need accents for voc
				{n={TILDE, "a"}, a={TILDE, "ǫ"}, g={TILDE, "ę̇"}, l={TILDE, "i"}, d={TILDE, "i"}, i={{TILDE, "ejǫ"}, {TILDE, "ǫ", footnote=contract_across_j_note}}, v={UNK, "e"}},
				{n={TILDE, "i"}, g={TILDE, "u"}, d={TILDE, "ama"}},
				{n={TILDE, "ę̇"}, a={TILDE, "ę̇"}, g={TILDE, "ь"}, l={{TILDE, "asъ"}, {TILDE, "axъ", footnote=a_stem_loc_pl_note}}, d={TILDE, "amъ"}, i={TILDE, "amī"}}
			)
		end
	elseif data.ap == "c" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for voc
			{n={"", "à"}, a={INVBREVE, "ǫ"}, g={"", "ę̇́"}, l={INVBREVE, "ī"}, d={"", "ì"}, i={"", "ejǫ́"}, v={UNK, "e"}},
			{n={INVBREVE, "i"}, g={"", "ù"}, d={"", "àma"}},
			{n={INVBREVE, "ę̇"}, a={INVBREVE, "ę̇"}, g={TILDE, "ь"}, l={{"", "àsъ"}, {"", "àxъ", footnote=a_stem_loc_pl_note}}, d={"", "àmъ"}, i={"", "àmi"}}
		)
	else
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a={UNK, "ǫ"}, g={UNK, "ę̇"}, l={UNK, "i"}, d={UNK, "i"}, i={{UNK, "ejǫ"}, {UNK, "ǫ", footnote=contract_across_j_note}}, v={LEAVE, "e"}},
			{n={UNK, "i"}, g={UNK, "u"}, d={UNK, "ama"}},
			{n={UNK, "ę̇"}, a={UNK, "ę̇"}, g={UNK, "ь"}, l={{UNK, "asъ"}, {UNK, "axъ", footnote=a_stem_loc_pl_note}}, d={UNK, "amъ"}, i={UNK, "ami"}}
		)
	end

	return forms, title
end

declensions["soft neuter o-stem"] = function(data)
	local forms = {}

	-- FIXME! No forms given for ap=a in Verweij
	if data.ap == "b" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for dual
			{n={MACRON, "è"}, a={MACRON, "è"}, g={MACRON, "à"}, l={MACRON, "ì"}, d={MACRON, "ù"}, i={{MACRON, "ь̀mь"}, {MACRON, "èmь", footnote=soft_o_stem_ins_sg_note}}, v={MACRON, "è"}},
			{n={TILDE, "i"}, g={UNK, "u"}, d={UNK, "ema"}}, -- The plural has neoacute throughout. Does the dual have this also?
			{n={TILDE, "a"}, a={TILDE, "a"}, g={TILDE, "ь"}, l={TILDE, "ixъ"}, d={TILDE, "emъ"}, i={TILDE, "i"}}
		)
	elseif data.ap == "c" then
		add_forms(data, forms, data.stem,
			{n={INVBREVE, "e"}, a={INVBREVE, "e"}, g={INVBREVE, "a"}, l={INVBREVE, "i"}, d={INVBREVE, "u"}, i={{INVBREVE, "ьmь"}, {INVBREVE, "emь", footnote=soft_o_stem_ins_sg_note}}, v={INVBREVE, "e"}},
			{n={INVBREVE, "i"}, g={"", "ù"}, d={"", "emà"}},
			{n={"", "à"}, a={"", "à"}, g={TILDE, "ь"}, l={"", "íxъ"}, d={"", "émъ"}, i={"", "í"}}
		)
	else
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a=data.nom_sg, g={UNK, "a"}, l={UNK, "i"}, d={UNK, "u"}, i={{UNK, "ьmь"}, {UNK, "emь", footnote=soft_o_stem_ins_sg_note}}, v=data.nom_sg},
			{n={UNK, "i"}, g={UNK, "u"}, d={UNK, "ema"}},
			{n={UNK, "a"}, a={UNK, "a"}, g={UNK, "ь"}, l={UNK, "ixъ"}, d={UNK, "emъ"}, i={UNK, "i"}}
		)
	end

	return forms, "soft o-stem", {"soft neuter o-stem nouns"}
end

declensions["hard neuter o-stem"] = function(data)
	local forms = {}

	if data.ap == "a" then
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a=data.nom_sg, g={GRAVE, "a"}, l={GRAVE, "ě", pal2=true}, d={GRAVE, "u"}, i={{GRAVE, "ъmь"}, {GRAVE, "omь", footnote=hard_o_stem_ins_sg_note}}, v=data.nom_sg},
			{n={GRAVE, "ě", pal2=true}, g={GRAVE, "u"}, d={GRAVE, "oma"}},
			{n={GRAVE, "a"}, a={GRAVE, "a"}, g={GRAVE, "ъ"}, l={GRAVE, "ě̄xъ", pal2=true}, d={GRAVE, "omъ"}, i={GRAVE, "ȳ"}}
		)
	elseif data.ap == "b" then
		add_forms(data, forms, data.stem,
			{n={MACRON, "ò"}, a={MACRON, "ò"}, g={MACRON, "à"}, l={MACRON, "ě̀", pal2=true}, d={MACRON, "ù"}, i={{MACRON, "ъ̀mь"}, {MACRON, "òmь", footnote=hard_o_stem_ins_sg_note}}, v={MACRON, "ò"}},
			{n={TILDE, "ě", pal2=true}, g={MACRON, "ù"}, d={MACRON, "òma"}},
			{n={MACRON, "à"}, a={MACRON, "à"}, g={TILDE, "ъ"}, l={TILDE, "ěxъ", pal2=true}, d={MACRON, "òmъ"}, i={TILDE, "y"}}
		)
	elseif data.ap == "c" then
		add_forms(data, forms, data.stem,
			{n={INVBREVE, "o"}, a={INVBREVE, "o"}, g={INVBREVE, "a"}, l={INVBREVE, "ě", pal2=true}, d={INVBREVE, "u"}, i={{INVBREVE, "ъmь"}, {INVBREVE, "omь", footnote=hard_o_stem_ins_sg_note}}, v=data.nom_sg},
			{n={INVBREVE, "ě", pal2=true}, g={"", "ù"}, d={"", "omà"}},
			{n={"", "à"}, a={"", "à"}, g={TILDE, "ъ"}, l={"", "ě̃xъ", pal2=true}, d={"", "òmъ"}, i={"", "ý"}}
		)
	else
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a=data.nom_sg, g={UNK, "a"}, l={UNK, "ě", pal2=true}, d={UNK, "u"}, i={{UNK, "ъmь"}, {UNK, "omь", footnote=hard_o_stem_ins_sg_note}}, v=data.nom_sg},
			{n={UNK, "ě", pal2=true}, g={UNK, "u"}, d={UNK, "oma"}},
			{n={UNK, "a"}, a={UNK, "a"}, g={UNK, "ъ"}, l={UNK, "ěxъ", pal2=true}, d={UNK, "omъ"}, i={UNK, "y"}}
		)
	end

	return forms, "hard o-stem", {"hard neuter o-stem nouns"}
end

declensions["hard masculine o-stem"] = function(data)
	local forms = {}

	if data.ap == "a" then
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a=data.nom_sg, g={GRAVE, "a"}, l={GRAVE, "ě", pal2=true}, d={GRAVE, "u"}, i={{GRAVE, "ъmь"}, {GRAVE, "omь", footnote=hard_o_stem_ins_sg_note}}, v={GRAVE, "e", pal1=true}},
			{n={GRAVE, "a"}, g={GRAVE, "u"}, d={GRAVE, "oma"}},
			{n={GRAVE, "i", pal2=true}, a={GRAVE, "y"}, g={GRAVE, "ъ"}, l={GRAVE, "ě̄xъ", pal2=true}, d={GRAVE, "omъ"}, i={GRAVE, "ȳ"}}
		)
	elseif data.ap == "b" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for vocative sg
			{n={TILDE, "ъ"}, a={TILDE, "ъ"}, g={MACRON, "à"}, l={MACRON, "ě̀", pal2=true}, d={MACRON, "ù"}, i={{MACRON, "ъ̀mь"}, {MACRON, "òmь", footnote=hard_o_stem_ins_sg_note}}, v={UNK, "e", pal1=true}},
			{n={MACRON, "à"}, g={MACRON, "ù"}, d={MACRON, "òma"}},
			{n={MACRON, "ì", pal2=true}, a={MACRON, "ỳ"}, g={TILDE, "ъ"}, l={TILDE, "ěxъ", pal2=true}, d={MACRON, "òmъ"}, i={TILDE, "y"}}
		)
	elseif data.ap == "c" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for vocative sg
			{n={INVBREVE, "ъ"}, a={INVBREVE, "ъ"}, g={INVBREVE, "a"}, l={INVBREVE, "ě", pal2=true}, d={INVBREVE, "u"}, i={{INVBREVE, "ъmь"}, {INVBREVE, "omь", footnote=hard_o_stem_ins_sg_note}}, v={UNK, "e", pal1=true}},
			{n={INVBREVE, "a"}, g={"", "ù"}, d={"", "omà"}},
			{n={INVBREVE, "i", pal2=true}, a={INVBREVE, "y"}, g={TILDE, "ъ"}, l={"", "ě̃xъ", pal2=true}, d={"", "òmъ"}, i={"", "ý"}}
		)
	else
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a=data.nom_sg, g={UNK, "a"}, l={UNK, "ě", pal2=true}, d={UNK, "u"}, i={{UNK, "ъmь"}, {UNK, "omь", footnote=hard_o_stem_ins_sg_note}}, v={UNK, "e", pal1=true}},
			{n={UNK, "a"}, g={UNK, "u"}, d={UNK, "oma"}},
			{n={UNK, "i", pal2=true}, a={UNK, "y"}, g={UNK, "ъ"}, l={UNK, "ěxъ", pal2=true}, d={UNK, "omъ"}, i={UNK, "y"}}
		)
	end

	return forms, "hard o-stem", {"hard masculine o-stem nouns"}
end

declensions["soft masculine o-stem"] = function(data)
	local forms = {}

	local voc_e = rfind(data.stem, "c$") or rfind(data.stem, "dz$") or
		rfind(data.stem, "sc$") or rfind(data.stem, "zdz$")

	if data.ap == "a" then
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a=data.nom_sg, g={GRAVE, "a"}, l={GRAVE, "i"}, d={GRAVE, "u"}, i={{GRAVE, "ьmь"}, {GRAVE, "emь", footnote=soft_o_stem_ins_sg_note}}, v={GRAVE, voc_e and "e" or "u", pal1=true}},
			{n={GRAVE, "a"}, g={GRAVE, "u"}, d={GRAVE, "ema"}},
			{n={GRAVE, "i"}, a={GRAVE, "ę̇"}, g={GRAVE, "ь"}, l={GRAVE, "īxъ"}, d={GRAVE, "ēmъ"}, i={GRAVE, "ī"}}
		)
	elseif data.ap == "b" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for vocative sg
			{n={TILDE, "ь"}, a={TILDE, "ь"}, g={MACRON, "à"}, l={MACRON, "ì"}, d={MACRON, "ù"}, i={{MACRON, "ь̀mь"}, {MACRON, "èmь", footnote=soft_o_stem_ins_sg_note}}, v={UNK, voc_e and "e" or "u", pal1=true}},
			{n={MACRON, "à"}, g={MACRON, "ù"}, d={MACRON, "èma"}},
			{n={MACRON, "ì"}, a={MACRON, "ę̇̀"}, g={TILDE, "ь"}, l={TILDE, "ixъ"}, d={TILDE, "emъ"}, i={TILDE, "i"}}
		)
	elseif data.ap == "c" then
		add_forms(data, forms, data.stem,
			-- FIXME! Need accents for vocative sg
			{n={INVBREVE, "ь"}, a={INVBREVE, "ь"}, g={INVBREVE, "a"}, l={INVBREVE, "i"}, d={INVBREVE, "u"}, i={{INVBREVE, "ьmь"}, {INVBREVE, "emь", footnote=soft_o_stem_ins_sg_note}}, v={UNK, voc_e and "e" or "u", pal1=true}},
			{n={INVBREVE, "a"}, g={"", "ù"}, d={"", "emà"}},
			{n={INVBREVE, "i"}, a={INVBREVE, "ę̇"}, g={TILDE, "ь"}, l={"", "ĩxъ"}, d={"", "èmъ"}, i={"", "í"}}
		)
	else
		add_forms(data, forms, data.stem,
			{n=data.nom_sg, a=data.nom_sg, g={UNK, "a"}, l={UNK, "i"}, d={UNK, "u"}, i={{UNK, "ьmь"}, {UNK, "emь", footnote=soft_o_stem_ins_sg_note}}, v={UNK, voc_e and "e" or "u", pal1=true}},
			{n={UNK, "a"}, g={UNK, "u"}, d={UNK, "ema"}},
			{n={UNK, "i"}, a={UNK, "ę̇"}, g={UNK, "ь"}, l={UNK, "ixъ"}, d={UNK, "emъ"}, i={UNK, "i"}}
		)
	end

	return forms, "soft o-stem", {"soft masculine o-stem nouns"}
end

return declensions