Module:bg-verb

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 export = {}


--[=[

Authorship: Ben Wing <benwing2>

]=]

--[=[

TERMINOLOGY:

-- "slot" = A particular combination of tense/aspect/person/number, or for participles
	 a particular combination of tense/aspect/voice/case/gender/number/definiteness.
	 Example slot names for nouns are "pres_1sg" (present first singular) and
	 "paap_def_obj_m_sg" (definite objective masculine singular past active aorist participle).
	 Each slot is filled with zero or more forms.

-- "form" = The conjugated Bulgarian form representing the value of a given slot.
	 For example, реши́лия is a form, representing the value of the paap_def_obj_m_sg
	 slot of the lemma реша́.

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

local lang = require("Module:languages").getByCode("bg")
local m_links = require("Module:links")
local m_table = require("Module:table")
local m_string_utilities = require("Module:string utilities")
local iut = require("Module:inflection utilities")
local m_para = require("Module:parameters")
local m_bg_translit = require("Module:bg-translit")
local com = require("Module:bg-common")

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

local u = require("Module:string/char")
local rsplit = mw.text.split
local rfind = mw.ustring.find
local rmatch = mw.ustring.match
local rgmatch = mw.ustring.gmatch
local rsubn = mw.ustring.gsub
local ulen = mw.ustring.len
local uupper = mw.ustring.upper

local AC = u(0x0301) -- acute =  ́


-- 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


local function link(term)
	return m_links.full_link({lang = lang, term = term, tr = "-"})
end


local verb_slots = {
	-- present tense
	"pres_1sg", "pres_2sg", "pres_3sg", "pres_1pl", "pres_2pl", "pres_3pl",
	-- imperfect
	"impf_1sg", "impf_2sg", "impf_3sg", "impf_1pl", "impf_2pl", "impf_3pl",
	-- aorist
	"aor_1sg", "aor_2sg", "aor_3sg", "aor_1pl", "aor_2pl", "aor_3pl",
	-- imperative
	"impv_sg", "impv_pl",
	-- present active participle
	"prap_ind_m_sg", "prap_def_sub_m_sg", "prap_def_obj_m_sg",
	"prap_ind_f_sg", "prap_def_f_sg", "prap_ind_n_sg", "prap_def_n_sg",
	"prap_ind_pl", "prap_def_pl",
	-- past active aorist participle
	"paap_ind_m_sg", "paap_def_sub_m_sg", "paap_def_obj_m_sg",
	"paap_ind_f_sg", "paap_def_f_sg", "paap_ind_n_sg", "paap_def_n_sg",
	"paap_ind_pl", "paap_def_pl",
	-- past active imperfect participle
	"paip_m_sg", "paip_f_sg", "paip_n_sg", "paip_pl",
	-- past passive participle
	"ppp_ind_m_sg", "ppp_def_sub_m_sg", "ppp_def_obj_m_sg",
	"ppp_ind_f_sg", "ppp_def_f_sg", "ppp_ind_n_sg", "ppp_def_n_sg",
	"ppp_ind_pl", "ppp_def_pl",
	-- verbal noun
	"vn_ind_sg", "vn_def_sg", "vn_ind_pl", "vn_def_pl",
	-- adverbial participle
	"advp",
}


local impers_verb_slots = m_table.listToSet({
	"pres_3sg",
	"impf_3sg",
	"aor_3sg",
	"prap_ind_n_sg", "prap_def_n_sg",
	"paap_ind_n_sg", "paap_def_n_sg",
	"paip_n_sg",
	"ppp_ind_n_sg", "ppp_def_n_sg",
	"vn_ind_sg", "vn_def_sg", "vn_ind_pl", "vn_def_pl",
	"advp",
})


local suppressed_acceleration_slots = m_table.listToSet({
	"vn_def_sg", "vn_ind_pl", "vn_def_pl",
})


local prefix_to_accel_form = {
	["pres"] = "pres|ind",
	["impf"] = "impf|ind",
	["aor"] = "aor|ind",
	["impv"] = "imp",
	["prap"] = "pres|act|part",
	["paap"] = "past|act|aor|part",
	["paip"] = "past|act|impf|part",
	["ppp"] = "past|pass|part",
	["vn"] = "vnoun",
	["advp"] = "adv|part",
}


local suffix_to_accel_form = {
	-- suffixes for finite forms
	["1sg"] = "1|s",
	["2sg"] = "2|s",
	["3sg"] = "3|s",
	["1pl"] = "1|p",
	["2pl"] = "2|p",
	["3pl"] = "3|p",
	-- additional suffixes for imperatives
	["sg"] = "s",
	["pl"] = "p",
	-- suffixes for participles that can have definite forms
	["ind_m_sg"] = "indef|m|s",
	["def_sub_m_sg"] = "def|sbj|m|s",
	["def_obj_m_sg"] = "def|obj|m|s",
	["ind_f_sg"] = "indef|f|s",
	["def_f_sg"] = "def|f|s",
	["ind_n_sg"] = "indef|n|s",
	["def_n_sg"] = "def|n|s",
	["ind_pl"] = "indef|p",
	["def_pl"] = "def|p",
	-- suffixes for participles that only have indefinite forms (i.e. imperfect participle)
	-- ("pl" already handled above)
	["m_sg"] = "m|s",
	["f_sg"] = "f|s",
	["n_sg"] = "n|s",
	-- additional verbal noun suffixes
	["ind_sg"] = "indef|s",
	["def_sg"] = "def|s",
}


local function slot_to_accel_form(slot)
	if suppressed_acceleration_slots[slot] then return nil end
	
	local prefix, suffix = rmatch(slot, "^([a-z]+)_(.*)$")
	if not prefix then
		return prefix_to_accel_form[slot]
	end
	return suffix_to_accel_form[suffix] .. "|" .. prefix_to_accel_form[prefix]
end


local verb_notr_slots = {
	"paap_ind_m_sg", "paap_ind_f_sg", "paap_ind_n_sg", "paap_ind_pl",
	"paip_m_sg", "paip_f_sg", "paip_n_sg", "paip_pl",
}


local base_slots = {
	"prap", -- masculine indefinite singular present active participle
	"paap", -- masculine indefinite singular past active aorist participle
	"paap_pl", -- masculine indefinite plural past active aorist participle
	"paip", -- masculine indefinite singular past active imperfect participle
	"advp", -- adverbial participle
	"pres", -- present 1sg
	"pres2", -- present 2sg
	"impf", -- imperfect 1sg
	"impf2", -- imperfect 2sg
	"aor", -- aorist 1sg
	"aor2", -- aorist 2sg
	"impv", -- imperative singular
	"vn", -- verbal noun
}


-- Used to determine if a perfective verb is prefixed; such verbs can't have
-- a stress-shifted aorist. This will have false positives; such verbs should
-- be indicated using (-pref).
local prefixes = {
	"^[дп]о", -- also catches под-
	"^[зн]а", -- also catches над-
	"^из",
	"^о" .. com.non_vowel_c, -- also catches об-, от-
	"^пр[еио]", -- also catches пред-
	"^раз",
	"^[вс][^аеиоуяю]", -- also catches въ-, съ–
	"^у",
}


local function verb_may_be_prefixed(lemma)
	for _, prefix in ipairs(prefixes) do
		if rfind(lemma, prefix) then
			return true
		end
	end
	return false
end


local function map_append(forms, suffix)
	return iut.map_form_or_forms(forms, function(form) return form .. suffix end)
end


local function map_rsub(forms, from, to)
	return iut.map_form_or_forms(forms, function(form) return rsub(form, from, to) end)
end


local function conjugate_all(base)
	local function combine_stem_ending(stem, ending)
		if stem == "?" then
			return "?"
		else
			return stem .. ending
		end
	end

	local function add(slot, stems, ending)
		if stems == nil then
			return
		end
		if type(stems) == "string" then
			iut.insert_form(base.forms, slot, {form = combine_stem_ending(stems, ending)})
		else
			if stems.form then
				stems = {stems}
			end
			for _, stem in ipairs(stems) do
				if type(stem) == "string" then
					stem = {form = stem}
				end
				iut.insert_form(base.forms, slot, {form = combine_stem_ending(stem.form, ending), footnotes = stem.footnotes})
			end
		end
	end

	local function conjugate_participle(baseslot, msg, fsg, nsg, pl)
		add(baseslot .. "_ind_m_sg", msg, "")
		add(baseslot .. "_def_sub_m_sg", pl, "ят")
		add(baseslot .. "_def_obj_m_sg", pl, "я")
		add(baseslot .. "_ind_f_sg", fsg, "")
		add(baseslot .. "_def_f_sg", fsg, "та")
		add(baseslot .. "_ind_n_sg", nsg, "")
		add(baseslot .. "_def_n_sg", nsg, "то")
		add(baseslot .. "_ind_pl", pl, "")
		add(baseslot .. "_def_pl", pl, "те")
	end

	local function conjugate_aor_impf(baseslot, stem, stem23)
		add(baseslot .. "_1sg", stem, "")
		add(baseslot .. "_2sg", stem23, "")
		add(baseslot .. "_3sg", stem23, "")
		add(baseslot .. "_1pl", stem, "ме")
		add(baseslot .. "_2pl", stem, "те")
		add(baseslot .. "_3pl", stem, "а")
	end

	if base.aspect ~= "pf" then
		conjugate_participle("prap", base.prap, map_append(base.prap, "а"),
			map_append(base.prap, "о"), map_append(base.prap, "и"))
	end
	conjugate_participle("paap", base.paap, base.paapf, base.paapn, base.paappl)
	if base.trans == "tr" then
		conjugate_participle("ppp", base.ppp, map_append(base.ppp, "а"), map_append(base.ppp, "о"),
			base.ppppl)
	end
	add("paip_m_sg", base.paip, "")
	add("paip_f_sg", base.paipf, "")
	add("paip_n_sg", base.paipn, "")
	add("paip_pl", base.paippl, "")
	if base.aspect ~= "pf" then
		add("vn_ind_sg", base.vn, "")
		add("vn_def_sg", base.vn, "то")
		-- Some verbal nouns are ending-stressed, but the plural in -ия pushes
		-- the stress onto the stem.
		add("vn_ind_pl", iut.map_form_or_forms(base.vn, function(form)
			return com.maybe_stress_final_syllable(rsub(form, "е́?$", ""))
		end), "ия")
		add("vn_def_pl", iut.map_form_or_forms(base.vn, function(form)
			return com.maybe_stress_final_syllable(rsub(form, "е́?$", ""))
		end), "ията")
		add("vn_ind_pl", base.vn, "та")
		add("vn_def_pl", base.vn, "тата")
	end
	add("pres_1sg", base.pres1sg, "")
	add("pres_2sg", base.pres2sg, "")
	add("pres_3sg", base.pres3sg, "")
	add("pres_1pl", base.pres1pl, "")
	add("pres_2pl", base.pres2pl, "")
	add("pres_3pl", base.pres3pl, "")
	conjugate_aor_impf("aor", base.aor, base.aor23)
	conjugate_aor_impf("impf", base.impf, base.impf23)
	add("impv_sg", base.impv, "")
	add("impv_pl", base.impvpl, "")
	if base.aspect ~= "pf" then
		add("advp", base.advp, "")
	end
end


local function add_reflexive_suffix(base)
	for _, slot in ipairs(verb_slots) do
		if not rfind(slot, "^vn_") then
			base.forms[slot] = iut.map_forms(base.forms[slot], function(form)
				return form .. " " .. base.refl
			end)
		end
	end
end


local function remove_non_impersonal_forms(base)
	for _, slot in ipairs(verb_slots) do
		if not impers_verb_slots[slot] then
			base.forms[slot] = nil
		end
	end
end


local function add_categories(base)
	base.categories = {}
	if base.aspect == "impf" then
		table.insert(base.categories, "Bulgarian imperfective verbs")
	elseif base.aspect == "pf" then
		table.insert(base.categories, "Bulgarian perfective verbs")
	else
		assert(base.aspect == "both")
		table.insert(base.categories, "Bulgarian imperfective verbs")
		table.insert(base.categories, "Bulgarian perfective verbs")
		table.insert(base.categories, "Bulgarian biaspectual verbs")
	end
	if base.trans == "tr" then
		table.insert(base.categories, "Bulgarian transitive verbs")
	elseif base.trans == "intr" then
		table.insert(base.categories, "Bulgarian intransitive verbs")
	end
	if base.refl then
		table.insert(base.categories, "Bulgarian reflexive verbs")
	end
	if base.irreg or base.conj == "irreg" then
		table.insert(base.categories, "Bulgarian irregular verbs")
	end
	if base.conj ~= "irreg" then
		table.insert(base.categories, "Bulgarian conjugation " .. base.conj .. " verbs")
	end
end


local function pres_advp_1conj(base, lemma)
	local stem, last_letter, accent = rmatch(lemma, "^(.*)(.)[ая](́?)$")
	if not stem then
		error("Unrecognized lemma for conjugation 1: '" .. lemma .. "'")
	end
	local last_letter_pal = com.first_palatalization[last_letter] or last_letter
	base.pres3sg = stem .. last_letter_pal .. "е" .. accent
	-- defaults to 1sg + т, but set it explicitly in case we're called
	-- from ям, дам, знам, with irregular 1sg
	base.pres3pl = lemma .. "т"
	base.advp = base.pres3sg .. "йки"
end


local function pres_advp_2conj(base, lemma)
	local stem, accent = rmatch(lemma, "^(.*)[ая](́?)$")
	if not stem then
		error("Unrecognized lemma for conjugation 2: '" .. lemma .. "'")
	end
	base.pres3sg = stem .. "и" .. accent
	base.advp = stem .. "е" .. accent .. "йки"
end


local function impf_12conj(base, lemma)
	local stem, last_letter, accent = rmatch(lemma, "^(.*)(.)[ая](́?)$")
	if not stem then
		error("Unrecognized lemma for conjugation 1 or 2: '" .. lemma .. "'")
	end
	local last_letter_pal = com.first_palatalization[last_letter] or last_letter
	local full_stem = stem .. last_letter_pal
	base.impf23 = full_stem .. "е" .. accent .. "ше"
	if accent == AC then
		if rfind(last_letter_pal, "[жчш]") then
			base.impf = {full_stem .. "а́х", {form = full_stem .. "е́х", footnotes = {"[largely fallen into disuse]"}}}
		else
			base.impf = full_stem .. "я́х"
			-- This is the only case where the imperfect participle's vowel
			-- disagrees with the imperfect 1sg vowel, since it's a yat vowel
			-- followed by a front vowel. In other cases, postprocess_base()
			-- will automatically generate the imperfect participle bases based
			-- on the imperfect 1sg.
			base.paippl = full_stem .. "е́ли"
		end
	else
		base.impf = full_stem .. "ех"
	end
end


local function generate_maybe_shifted_aorist(base, aor23, shifted_aor23)
	if base.aspect ~= "pf" or not base.prefixed then
		shifted_aor23 = shifted_aor23 or rsub(aor23, AC, "") .. AC
		if aor23 ~= shifted_aor23 then
			base.aor23 = {aor23, {form = shifted_aor23, footnotes = {"[dialectally marked]"}}}
		else
			base.aor23 = aor23
		end
	else
		base.aor23 = aor23
	end
end


local function impv_12conj(base, lemma)
	local stem, last_letter = rmatch(lemma, "^(.-)(.)́?[ая]́?$")
	if not stem then
		error("Unrecognized lemma for conjugation 1 or 2: '" .. lemma .. "'")
	end
	last_letter = com.first_palatalization[last_letter] or last_letter
	local full_stem = stem .. last_letter
	if rfind(last_letter, com.vowel_c) then
		base.impv = com.maybe_stress_final_syllable(full_stem) .. "й"
	else
		full_stem = rsub(full_stem, AC, "")
		base.impv = full_stem .. "и́"
		base.impvpl = full_stem .. "е́те"
	end
end


local function impf_impv_12conj(base, lemma)
	impf_12conj(base, lemma)
	impv_12conj(base, lemma)
end


local conjs = {}


conjs["1.1"] = function(base, lemma)
	pres_advp_1conj(base, lemma)
	impf_impv_12conj(base, lemma)

	local stem, last_letter = rmatch(lemma, "^(.*)(.)а́?$")
	if not stem then
		error("Unrecognized lemma for class 1.1: '" .. lemma .. "'")
	end
	stem = com.maybe_stress_final_syllable(stem)

	-- Generate aorist stems (and various other stems for вля́за).
	local last_letter_pal = com.first_palatalization[last_letter] or last_letter
	if rfind(lemma, "ля́за$") then
		local full_stem = stem .. last_letter
		local unyat_stem = rsub(full_stem, "ля́з$", "ле́з")
		base.aor = full_stem .. "ох"
		base.aor23 = unyat_stem .. "е"
		base.impf = unyat_stem .. "ех"
		base.impf23 = unyat_stem .. "еше"
		base.pres3sg = unyat_stem .. "е"
		base.impv = unyat_stem
		base.irreg = true
	elseif rfind(lemma, "[бв]лека́$") or rfind(lemma, "сека́$") then
		base.aor = rsub(stem, "^(.*)е", "%1я") .. last_letter .. "ох"
		base.aor23 = stem .. last_letter_pal .. "е"
	else
		base.aor = stem .. last_letter .. "ох"
		base.aor23 = stem .. last_letter_pal .. "е"
	end

	-- Generate aorist participle stems.
	if rfind(lemma, "раста́$") then
		base.paap = stem .. "ъл"
		base.paapf = stem .. "ла"
		base.irreg = true
	elseif last_letter == "д" or last_letter == "т" then
		base.paap = stem .. "л"
	else
		local full_stem = rsub(base.aor, "ох$", "")
		base.paap = full_stem .. "ъл"
		base.paapf = full_stem .. "ла"
		base.paappl = rsub(full_stem, "я", "е") .. "ли"
	end

	-- Generate past passive participle stems.
	base.ppp = stem .. last_letter_pal .. "ен"

	-- Generate verbal noun stems.
	base.vna = base.aor23 .. "не"
end


conjs["1.2"] = function(base, lemma)
	pres_advp_1conj(base, lemma)
	impf_impv_12conj(base, lemma)

	if not rfind(lemma, "а́?$") then
		error("Unrecognized lemma for class 1.2: '" .. lemma .. "'")
	end

	-- Generate aorist stems.
	local aor23
	if rfind(lemma, "ера́$") then
		-- бера́, дера́, пера́ and derivatives
		aor23 = rsub(lemma, "ера́$", "ра́")
	elseif rfind(lemma, "греба́$") then
		aor23 = rsub(lemma, "греба́$", "гре́ба")
		base.irreg = true
	elseif rfind(lemma, "гриза́$") then
		aor23 = rsub(lemma, "гриза́$", "гри́за")
		base.irreg = true
	elseif rfind(lemma, "я́") then
		-- дя́на (Chitanka type 153tt)
		-- бя́лна се, дя́лна, избя́гна, мля́сна, мя́рна, отбя́гна, пробя́гна, ря́зна (Chitanka type 152att)
		-- забя́гна, кря́кна, кря́сна, побя́гна, прибя́гна, убя́гна (Chitanka type 152ait)
		local unyat_stem = rsub(lemma, "я́(.*)а$", "е́%1")
		local unstressed_unyat_stem = rsub(unyat_stem, "е́", "е")
		local shifted_aor23 = unstressed_unyat_stem .. "а́"
		generate_maybe_shifted_aorist(base, lemma, shifted_aor23)
		base.impf = unyat_stem .. "ех"
		base.impf23 = unyat_stem .. "еше"
		base.pres3sg = unyat_stem .. "е"
		base.impv = unstressed_unyat_stem .. "и́"
		base.impvpl = unstressed_unyat_stem .. "е́те"
		base.irreg = true
	else
		generate_maybe_shifted_aorist(base, lemma)
	end
	if aor23 then
		base.aor23 = aor23
	end

	-- Generate past passive participle stems.
	base.ppp = aor23 and aor23 .. "н" or rfind(lemma, "на́?$") and lemma .. "т" or lemma .. "н"

	-- Generate verbal noun stems.
	if rfind(lemma, "на́?$") then
		base.vna = false
	elseif rfind(lemma, "ера́$") then
		base.vna = rsub(lemma, "ера́$", "ране́")
		base.vni = false
	end
end


conjs["1.3"] = function(base, lemma)
	pres_advp_1conj(base, lemma)
	impf_impv_12conj(base, lemma)

	if not rfind(lemma, "я́?$") then
		error("Unrecognized lemma for class 1.3: '" .. lemma .. "'")
	end

	-- Generate aorist stems.
	local aor23
	if rfind(lemma, "дре́мя$") then
		aor23 = rsub(lemma, "дре́мя$", "дря́ма")
		local shifted_aor23 = rsub(lemma, "дре́мя$", "дрема́")
		generate_maybe_shifted_aorist(base, aor23, shifted_aor23)
		base.irreg = true
	else
		aor23 = rsub(lemma, "^(.*)я", "%1а")
		generate_maybe_shifted_aorist(base, rsub(lemma, "^(.*)я", "%1а"))
	end

	-- Generate past passive participle stems.
	base.ppp = aor23 .. "н"
end


conjs["1.4"] = function(base, lemma)
	pres_advp_1conj(base, lemma)
	impf_impv_12conj(base, lemma)

	-- Generate aorist stems.
	local aor23
	local skip_generate_aorist = false
	if rfind(lemma, "лъ́жа$") or rfind(lemma, "стри́жа$") or rfind(lemma, "стъ́ржа$") then
		aor23 = rsub(lemma, "жа$", "га")
	elseif rfind(lemma, "ре́жа$") then
		aor23 = rsub(lemma, "ре́жа$", "ря́за")
		local shifted_aor23 = rsub(lemma, "ре́жа", "реза́")
		generate_maybe_shifted_aorist(base, aor23, shifted_aor23)
		skip_generate_aorist = true
		base.irreg = true
	elseif rfind(lemma, "жа$") then
		aor23 = rsub(lemma, "жа$", "за")
	elseif rfind(lemma, "ча$") then
		aor23 = rsub(lemma, "ча$", "ка")
	elseif rfind(lemma, "ша$") then
		aor23 = rsub(lemma, "ша$", "са")
	elseif rfind(lemma, "ща$") then -- тра́ща
		aor23 = rsub(lemma, "ща$", "та")
	else
		error("Unrecognized lemma for class 1.4: '" .. lemma .. "'")
	end
	if not skip_generate_aorist then
		generate_maybe_shifted_aorist(base, aor23)
	end

	-- Generate past passive participle stems.
	base.ppp = aor23 .. "н"
end


conjs["1.5"] = function(base, lemma)
	pres_advp_1conj(base, lemma)
	impf_impv_12conj(base, lemma)

	if not rfind(lemma, "[рщ]а́$") then
		error("Unrecognized lemma for class 1.5: '" .. lemma .. "'")
	end

	-- Generate aorist stems.
	base.aor23 = rsub(lemma, "а́$", "я́")
	base.paappl = rsub(lemma, "а́$", "е́ли")

	-- Generate past passive participle stems.
	base.ppp = base.aor23 .. "н"
	base.ppppl = rsub(base.aor23, "я́$", "е́") .. "ни"
end


conjs["1.6"] = function(base, lemma)
	pres_advp_1conj(base, lemma)
	impf_impv_12conj(base, lemma)

	if not rfind(lemma, "[аяе]́я$") then
		error("Unrecognized lemma for class 1.6: '" .. lemma .. "'")
	end

	-- Generate aorist stems.
	generate_maybe_shifted_aorist(base, lemma)

	-- Generate past passive participle stems.
	if rfind(lemma, "зна́я$") then
		base.ppp = rsub(lemma, "я$", "ен")
		base.irreg = true
	else
		base.ppp = lemma .. "н"
	end
end


conjs["1.7"] = function(base, lemma)
	pres_advp_1conj(base, lemma)
	impf_impv_12conj(base, lemma)

	local ppp_endings
	if base.ppp_ending == "т" then
		ppp_endings = "т"
	elseif base.ppp_ending == "тн" then
		ppp_endings = {"т", "н"}
	elseif base.ppp_ending == "нт" then
		ppp_endings = {"н", "т"}
	else
		ppp_endings = "н"
	end

	-- Generate aorist and past passive participle stems.
	if rfind(lemma, "ма$") then
		base.aor23 = rsub(lemma, "ма$", "")
		base.ppp = base.aor23 .. "т"
	elseif rfind(lemma, "[жчш]е́я$") then
		base.aor23 = rsub(lemma, "е́я$", "а́")
		base.ppp = iut.map_form_or_forms(ppp_endings, function(ending) return base.aor23 .. ending end)
	elseif rfind(lemma, "е́я$") then
		base.aor23 = rsub(lemma, "е́я$", "я́")
		local yat_plural_stem = rsub(lemma, "е́я$", "е́")
		base.paappl = yat_plural_stem .. "ли"
		base.ppp = iut.map_form_or_forms(ppp_endings, function(ending) return base.aor23 .. ending end)
		base.ppppl = iut.map_form_or_forms(ppp_endings, function(ending) return yat_plural_stem .. ending .. "и" end)
	elseif rfind(lemma, "[аяиую]́я$") then
		-- For verbs in -я́я (влия́я, сия́я), this results in an aorist participle
		-- in -я́л, but per rechnik.chitanka.info it stays as -я́ли in the plural,
		-- not **-е́ли.
		base.aor23 = rsub(lemma, "я$", "")
		if rfind(lemma, "[иую]́я$") then
			base.ppp = base.aor23 .. "т"
		else
			base.ppp = iut.map_form_or_forms(ppp_endings, function(ending) return base.aor23 .. ending end)
		end
	else
		error("Unrecognized lemma for class 1.7: '" .. lemma .. "'")
	end

	-- Generate verbal noun stems.
	if rfind(lemma, "[еиую]́я$") then
		base.vna = false
	end
end


conjs["2.1"] = function(base, lemma)
	pres_advp_2conj(base, lemma)
	impf_impv_12conj(base, lemma)

	-- Generate aorist stems.
	generate_maybe_shifted_aorist(base, rsub(lemma, "^(.*)[ая]", "%1и"))

	-- Generate past passive participle stems.
	base.ppp = rsub(lemma, "^(.*)[ая](́?)$", "%1е%2н")

	-- Generate verbal noun stems.
	base.vna = base.ppp .. "е"
	base.vni = false
end


conjs["2.2"] = function(base, lemma)
	pres_advp_2conj(base, lemma)
	impf_impv_12conj(base, lemma)

	if not rfind(lemma, "я́$") then
		error("Unrecognized lemma for class 2.2: '" .. lemma .. "'")
	end

	-- Generate aorist stems.
	base.aor23 = lemma
	local oya = rfind(lemma, "оя́$") -- стоя́, боя́ се
	local yat_plural_stem = oya and lemma or rsub(lemma, "я́$", "е́")
	base.paappl = yat_plural_stem .. "ли"

	if oya then
		-- стоя has both стоя́не and стое́не as verbal nouns
		base.vna = lemma .. "не"
		base.paippl = lemma .. "ли"
		base.irreg = true
	end

	-- Generate past passive participle stems.
	base.ppp = base.aor23 .. "н"
	base.ppppl = yat_plural_stem .. "ни"
end


conjs["2.3"] = function(base, lemma)
	pres_advp_2conj(base, lemma)
	impf_impv_12conj(base, lemma)

	if not rfind(lemma, "[жчш]а́$") then
		error("Unrecognized lemma for class 2.3: '" .. lemma .. "'")
	end

	-- Generate aorist stems.
	base.aor23 = lemma

	-- Generate past passive participle stems.
	base.ppp = base.aor23 .. "н"

	-- Handle irregularities
	if rfind(lemma, "държа́$") then
		base.impv = rsub(lemma, "държа́$", "дръ́ж")
		base.irreg = true
	end
end


conjs["3"] = function(base, lemma)
	local stem = rmatch(lemma, "^(.*[ая])м$")
	if not stem then
		error("Unrecognized lemma for conjugation 3: '" .. lemma .. "'")
	end

	-- Generate present stems.
	base.pres3sg = stem
	base.pres1pl = stem .. "ме"
	base.pres3pl = stem .. "т"

	-- Generate imperfect stems.
	base.impf = stem .. "х"
	base.impf23 = stem .. "ше"

	-- Generate aorist stems.
	generate_maybe_shifted_aorist(base, stem)

	-- Generate imperative stems.
	base.impv = stem .. "й"

	-- Generate past passive participle stems.
	base.ppp = stem .. "н"

	-- Generate adverbial participle.
	base.advp = stem .. "йки"
end


conjs["irreg"] = function(base, lemma)
	base.irreg = true
	if rfind(lemma, "я́м$") then
		conjs["1.1"](base, rsub(lemma, "я́м$", "яда́"))
		base.vni = false
		base.impv = rsub(lemma, "м$", "ж")
		base.conj = "1.1"
	elseif rfind(lemma, "съ́м$") then
		local stem = rmatch(lemma, "^(.*)съ́м$")
		base.pres2sg = stem .. "си́"
		base.pres3sg = stem .. "е́"
		base.pres1pl = stem .. "сме́"
		base.pres2pl = stem .. "сте́"
		base.pres3pl = stem .. "са́"
		base.aor = stem .. "бя́х"
		base.aor23 = {stem .. "бе́", stem .. "бе́ше"}
		base.impf = stem .. "бя́х"
		base.impf23 = stem .. "бе́ше"
		base.prap = false
		base.paap = stem .. "би́л"
		base.paapf = stem .. "била́"
		base.paip = stem .. "би́л"
		base.paipf = stem .. "била́"
		base.advp = {stem .. "бъ́дейки", stem .. "би́дейки"}
		base.impv = "бъди́"
		base.impvpl = "бъде́те"
		base.vna = false
		base.vni = false
	elseif rfind(lemma, "бъ́да$") then
		local stem = rmatch(lemma, "^(.*)бъ́да$")
		base.pres3sg = stem .. "бъ́де"
		base.aor = {stem .. "би́х", stem .. "би́дох"}
		base.aor23 = {stem .. "би́", stem .. "би́де"}
		base.impf = {stem .. "бъ́дех", stem .. "бя́х"}
		base.impf23 = {stem .. "бъ́деше", stem .. "бе́ше"}
		base.prap = stem .. "бъ́дещ"
		base.paap = stem .. "би́л"
		base.paapf = stem .. "била́"
		base.paip = stem .. "бъ́дел"
		base.advp = {stem .. "бъ́дейки", stem .. "би́дейки"}
		base.impv = "бъди́"
		base.impvpl = "бъде́те"
		base.vna = false
		base.vni = false
	elseif rfind(lemma, "ща́") then
		local stem = rmatch(lemma, "^(.*)ща́$")
		base.pres3sg = stem .. "ще́"
		base.aor = stem .. "щя́х"
		base.aor23 = {stem .. "щя́", stem .. "ще́ше"}
		base.impf = stem .. "щя́х"
		base.impf23 = stem .. "ще́ше"
		base.prap = false
		base.paap = stem .. "щя́л"
		base.paappl = stem .. "ще́ли"
		base.paip = stem .. "щя́л"
		base.paippl = stem .. "ще́ли"
		base.vna = false
		base.vni = false
	elseif rfind(lemma, "зна́м$") then
		conjs["1.6"](base, rsub(lemma, "а́м$", "а́я"))
		base.conj = "1.6"
	elseif rfind(lemma, "да́м$") then
		conjs["1.1"](base, rsub(lemma, "да́м$", "дада́"))
		base.impv = rsub(lemma, "м$", "й")
		base.conj = "1.1"
	elseif rfind(lemma, "йда$") then -- до́йда, за́йда, подо́йда, придо́йда
		pres_advp_1conj(base, lemma)
		impf_impv_12conj(base, lemma)
		base.aor = rsub(lemma, AC .. "йда$", "йдо́х")
		base.aor23 = rsub(lemma, AC .. "йда$", "йде́")
		base.paap = rsub(lemma, AC .. "йда$", "шъ́л")
		base.paapf = rsub(lemma, AC .. "йда$", "шла́")
		if lemma == "до́йда" then
			base.impv = "ела́"
			base.impvpl = base.impv .. "те"
		end
		-- no past passive participle
		base.vna = false
		base.conj = "1.1"
	elseif rfind(lemma, "и́да$") then -- и́да, оти́да, пооти́да, разоти́да
		pres_advp_1conj(base, lemma)
		impf_impv_12conj(base, lemma)
		if lemma ~= "и́да" then
			-- base verb и́да doesn't have aorist or aorist participle forms
			base.aor = rsub(lemma, "да$", "дох")
			base.aor23 = rsub(lemma, "да$", "де")
			base.paap = {rsub(lemma, "да$", "шъл"), rsub(lemma, "и́да$", "ишъ́л"),
				{form=rsub(lemma, "да$", "шел"), footnotes={"[dialectal]"}}}
			base.paapf = {rsub(lemma, "да$", "шла"), rsub(lemma, "и́да$", "ишла́")}
		end
		-- no past passive participle, no verbal noun
		base.vna = false
		base.vni = false
		base.conj = "1.1"
	elseif rfind(lemma, "мо́га$") then
		pres_advp_1conj(base, lemma)
		impf_12conj(base, lemma)
		-- no imperative
		base.aor23 = rsub(lemma, "мо́га$", "можа́")
		local reg_aor23 = rsub(lemma, "мо́га$", "можа́л")
		base.paap = {rsub(lemma, "мо́га$", "могъ́л"), reg_aor23}
		base.paapf = {rsub(lemma, "мо́га$", "могла́"), reg_aor23 .. "а"}
		-- no past passive participle
		base.vna = false
	elseif rfind(lemma, "спя́$") then
		pres_advp_2conj(base, lemma)
		impf_impv_12conj(base, lemma)
		base.aor23 = rsub(lemma, "я́$", "а́")
		-- no past passive participle
		base.vna = rsub(lemma, "я́$", "ане́")
		base.vni = false
	elseif rfind(lemma, "ме́ля$") then
		local stem = rmatch(lemma, "^(.*)я$")
		base.pres3sg = {stem .. "е", stem .. "и"}
		base.advp = stem .. "ейки"
		impf_impv_12conj(base, lemma)
		base.aor23 = rsub(lemma, "е́ля$", "ля́")
		local yat_plural_stem = rsub(lemma, "е́ля$", "ле́")
		base.paappl = yat_plural_stem .. "ли"
		base.ppp = base.aor23 .. "н"
		base.ppppl = yat_plural_stem .. "ни"
		base.vna = false
		-- prefixed variants are perfective and don't have verbal nouns
		base.vni = {"ме́лене", {form="мле́не", footnotes={"[colloquial]"}}, {form="млене́", footnotes={"[colloquial]"}}}
	elseif rfind(lemma, "ко́ля$") then
		pres_advp_2conj(base, lemma)
		impf_impv_12conj(base, lemma)
		base.aor23 = {rsub(lemma, "о́ля$", "ла́"), rsub(lemma, "я$", "и")}
		base.ppp = {rsub(lemma, "о́ля$", "ла́н"), rsub(lemma, "я$", "ен")}
		base.vna = false
		-- prefixed variants are perfective and don't have verbal nouns
		base.vni = {"ко́лене", "клане́"}
	elseif rfind(lemma, "ви́дя$") then
		pres_advp_2conj(base, lemma)
		impf_impv_12conj(base, lemma)
		base.aor23 = rsub(lemma, "ви́дя$", "видя́")
		local yat_plural_stem = rsub(base.aor23, "я́$", "е́")
		base.paappl = yat_plural_stem .. "ли"
		-- Generate past passive participle stems.
		base.ppp = base.aor23 .. "н"
		base.ppppl = yat_plural_stem .. "ни"
		-- Generate imperative forms.
		base.impv = rsub(lemma, "ви́дя$", "ви́ж")
		-- perfective; no verbal noun
		base.vna = false
		base.vni = false
		base.conj = "2.2"
	elseif rfind(lemma, "кълна́$") then
		pres_advp_1conj(base, lemma)
		impf_impv_12conj(base, lemma)
		base.aor23 = {rsub(lemma, "кълна́$", "кле́"), lemma}
		base.ppp = {rsub(lemma, "кълна́$", "кле́т"), lemma .. "т"}
		base.vna = false
		base.conj = "1.2"
	elseif rfind(lemma, "сте́ля$") then
		pres_advp_1conj(base, lemma)
		impf_impv_12conj(base, lemma)
		base.aor23 = rsub(lemma, "сте́ля$", "стла́")
		base.ppp = base.aor23 .. "н"
		base.vna = false
		base.conj = "1.3"
	elseif rfind(lemma, "беле́жа$") then
		pres_advp_2conj(base, lemma)
		impf_impv_12conj(base, lemma)
		base.aor23 = rsub(lemma, "ле́жа$", "ля́за")
		base.ppp = rsub(lemma, "ле́жа$", "ля́зан")
		base.vna = false
	else
		error("Irregular verb '" .. lemma .. "' not yet supported")
	end
end


local function postprocess_base(base, lemma)
	if not base.pres1sg then
		base.pres1sg = lemma
	end
	if not base.pres2sg then
		base.pres2sg = map_append(base.pres3sg, "ш")
	end
	if not base.pres1pl then
		base.pres1pl = map_append(base.pres3sg, "м")
	end
	if not base.pres2pl then
		base.pres2pl = map_append(base.pres3sg, "те")
	end
	if not base.pres3pl then
		base.pres3pl = map_append(base.pres1sg, "т")
	end
	if not base.aor then
		base.aor = map_append(base.aor23, "х")
	end
	if base.prap == false then
		base.prap = nil
	elseif not base.prap then
		base.prap = map_rsub(base.impf, "х$", "щ")
	end
	if not base.paap then
		base.paap = map_rsub(base.aor, "х$", "л")
	end
	if not base.paapf then
		base.paapf = map_append(base.paap, "а")
	end
	if not base.paapn then
		base.paapn = map_rsub(base.paapf, "а(́?)$", "о%1")
	end
	if not base.paappl then
		base.paappl = map_rsub(base.paapf, "а(́?)$", "и%1")
	end
	if not base.paip then
		base.paip = map_rsub(base.impf, "х$", "л")
	end
	if not base.paipf then
		base.paipf = map_append(base.paip, "а")
	end
	if not base.paipn then
		base.paipn = map_rsub(base.paipf, "а(́?)$", "о%1")
	end
	if not base.paippl then
		base.paippl = map_rsub(base.paipf, "а(́?)$", "и%1")
	end
	if not base.ppppl then
		base.ppppl = map_append(base.ppp, "и")
	end
	if not base.impvpl then
		base.impvpl = map_append(base.impv, "те")
	end
	local function aor_to_vn(form)
		form = rsub(form, "я́х$", "е́не")
		form = rsub(form, "х$", "не")
		return form
	end
	local function impf_to_vn(form)
		form = rsub(form, "[ая]́х$", "е́не")
		form = rsub(form, "х$", "не")
		return form
	end
	if base.vna == nil then
		base.vna = iut.map_form_or_forms(base.aor, aor_to_vn, "first only")
	end
	if base.vni == nil then
		base.vni = iut.map_form_or_forms(base.impf, impf_to_vn, "first only")
	end
	if base.vna == false and base.vni == false then
		base.vn = nil
	elseif base.vna == false then
		base.vn = base.vni
	elseif base.vni == false then
		base.vn = base.vna
	elseif base.vna == base.vni then
		base.vn = base.vna
	elseif base.aspect == "pf" then
		-- No verbal noun.
		base.vn = nil
	elseif base.vnspec == "a" then
		base.vn = base.vna
	elseif base.vnspec == "i" then
		base.vn = base.vni
	elseif base.vnspec == "ai" then
		base.vn = {base.vna, base.vni}
	elseif base.vnspec == "ia" then
		base.vn = {base.vni, base.vna}
	elseif base.vnspec == "?" then
		base.vn = "?"
	else
		error("Imperfective/biaspectual verb '" .. base.full_lemma .. "' has two possible verbal nouns, don't know which one to choose; specify 'vna' for " .. base.vna .. ", 'vni' for " .. base.vni .. ", or 'vnai' or 'vnia' for both")
	end
end


local function parse_indicator_and_form_spec(angle_bracket_spec)
	local inside = rmatch(angle_bracket_spec, "^<(.*)>$")
	assert(inside)
	local base = {}
	local parts = rsplit(inside, ".", true)
	local conj
	local start = 1
	if parts[1] == "irreg" then
		conj = "irreg"
		start = 2
	elseif rfind(parts[1], "^[123]$") then
		conj = parts[1]
		start = 2
		if parts[2] and rfind(parts[2], "^[1-7]$") then
			conj = conj .. "." .. parts[2]
			start = 3
		end
	end
	base.conj = conj
	for i=start,#parts do
		local part = parts[i]
		if part == "impf" or part == "pf" or part == "both" then
			if base.aspect then
				error("Can't specify aspect twice: '" .. inside .. "'")
			end
			base.aspect = part
		elseif part == "tr" or part == "intr" then
			if base.trans then
				error("Can't specify transitivity twice: " .. inside .. "'")
			end
			base.trans = part
		elseif part == "т" or part == "тн" or part == "нт" then
			if base.ppp_ending then
				error("Can't specify past passive participle ending twice: " .. inside .. "'")
			end
			base.ppp_ending = part
		elseif part == "-pref" then
			if base.no_pref then
				error("Can't specify '-pref' twice: " .. inside .. "'")
			end
			base.no_pref = true
		elseif part == "vna" or part == "vni" or part == "vnai" or part == "vnia" or part == "vn?" then
			if base.vnspec then
				error("Can't specify verbal noun spec twice: " .. inside .. "'")
			end
			base.vnspec = rsub(part, "^vn", "")
		elseif part == "impers" then
			if base.impers then
				error("Can't specify 'impers' twice: " .. inside .. "'")
			end
			base.impers = true
		else
			error("Unrecognized indicator '" .. part .. "': '" .. inside .. "'")
		end
	end
	return base
end


-- Separate out reflexive suffix, check that multisyllabic lemmas have stress, and add stress
-- to monosyllabic lemmas if needed.
local function check_lemma_stress(base, lemma)
	local active_verb, refl = rmatch(lemma, "^(.*) (с[еи])$")
	if active_verb then
		base.refl = refl
		lemma = active_verb
	end
	lemma = com.add_monosyllabic_stress(lemma)
	if not rfind(lemma, AC) then
		error("Multisyllabic lemma '" .. lemma .. "' needs an accent")
	end
	if base.refl then
		base.full_lemma = lemma .. " " .. base.refl
	else
		base.full_lemma = lemma
	end
	return lemma
end


local function impersonal_to_personal_lemma(lemma, conj)
	local undo_first_palatalization = {
		["ч"] = "к",
		["ж"] = "г",
		["ш"] = "х",
	}

	local function mustsub(from, to)
		local newlemma = rsub(lemma, from, to)
		if newlemma == lemma then
			error("Unrecognized impersonal lemma for conjugation " .. conj .. ": " .. lemma)
		end
		return newlemma
	end

	local conj_is_2 = rfind(conj, "^2%.")
	if conj == "1.1" then
		return mustsub("(.)е(́?)$",
			function(last_cons, accent)
				return (undo_first_palatalization[last_cons] or last_cons) .. "а" .. accent
			end)
	elseif conj == "1.2" or conj == "1.4" or conj == "1.5" or (conj == "1.7" and rfind(lemma, "ме$")) then
		return mustsub("е(́?)$", "а%1")
	elseif conj == "1.3" or conj == "1.6" or conj == "1.7" then
		return mustsub("е(́?)$", "я%1")
	elseif (conj_is_2 and rfind(lemma, "[чжш]е$")) then
		return mustsub("и(́?)$", "а%1")
	elseif conj_is_2 then
		return mustsub("и(́?)$", "я%1")
	elseif rfind(conj, "^3") then
		return lemma .. "м"
	else
		error("Can't handle irregular impersonal verbs yet")
	end
end


local function detect_indicator_and_form_spec(base, lemma)
	if not base.aspect then
		error("Aspect of 'pf', 'impf' or 'both' must be specified")
	end
	if base.refl then
		if base.trans then
			error("Can't specify transitivity with reflexive verb, they're always intransitive: '" .. base.full_lemma .. "'")
		end
	elseif not base.trans then
		error("Transitivity of 'tr' or 'intr' must be specified")
	end
	local pers_ending = base.impers and "" or "м"
	if not base.conj then
		if rfind(lemma, "[ая]" .. pers_ending .. "$") then
			base.conj = "3"
		elseif base.impers then
			error("For lemma ending in -е or -и, conjugation must be specified: '" .. lemma .. "'")
		else
			error("For lemma ending in -а or -я, conjugation must be specified: '" .. lemma .. "'")
		end
	elseif base.conj == "3.1" then
		if not rfind(lemma, "а" .. pers_ending .. "$") then
			error("Conjugation 3.1 lemma must end in -а" .. pers_ending .. ": '" .. lemma .. "'")
		end
		base.conj = "3"
	elseif base.conj == "3.2" then
		if not rfind(lemma, "я" .. pers_ending .. "$") then
			error("Conjugation 3.2 lemma must end in -я" .. pers_ending .. ": '" .. lemma .. "'")
		end
		base.conj = "3"
	elseif not conjs[base.conj] then
		error("Unrecognized conjugation '" .. base.conj .. "' for lemma '" .. lemma .. "'")
	end
	base.orig_lemma = lemma
	if base.impers then
		lemma = impersonal_to_personal_lemma(lemma, base.conj)
	end
	base.prefixed = not base.no_pref and verb_may_be_prefixed(lemma)
	return lemma
end


local function parse_word_spec(text)
	local segments = iut.parse_balanced_segment_run(text, "<", ">")
	if #segments ~= 3 or segments[3] ~= "" then
		error("Verb spec must be of the form 'LEMMA<CONJ.SPECS>': '" .. text .. "'")
	end
	local lemma = segments[1]
	local base = parse_indicator_and_form_spec(segments[2])
	return base, lemma
end


local function format_gender(g)
	return require("Module:gender and number").format_list({g})
end


local function show_forms(base, fullmod)
	local forms = base.forms
	local lemmas = {}
	for _, lemma in ipairs(forms.lemma) do
		table.insert(lemmas, com.remove_monosyllabic_stress(lemma))
	end
	local accel_lemma = lemmas[1]
	forms.lemma = table.concat(lemmas, ", ")

	local footnote_obj = com.init_footnote_obj()

	for _, notr_slot in ipairs(verb_notr_slots) do
		forms[notr_slot .. "_notr"] = com.display_one_form(footnote_obj, forms, notr_slot,
			nil, nil, false, "slash join")
	end
	com.display_forms(footnote_obj, forms, forms, verb_slots, "is list", accel_lemma, slot_to_accel_form)
	if fullmod then
		com.display_forms(footnote_obj, forms, forms, fullmod.verb_compound_slots, "is list", nil, nil)
	end
	forms.refl = base.refl or ""
	-- forms.along_with_refl = base.refl and "along with [[" .. base.refl .. "]]" or ""
	forms.along_with_refl = ""
	forms.gm = format_gender("m")
	forms.gf = format_gender("f")
	forms.gn = format_gender("n")
	forms.gp = format_gender("p")
	if base.footnote then
		table.insert(footnote_obj.notes, base.footnote)
	end
	forms.footnote = table.concat(footnote_obj.notes, "<br />")
end


local function make_table(base, fullmod)
	local forms = base.forms

	local ann_parts = {}
	if base.conj == "irreg" then
		table.insert(ann_parts, "irregular")
	else
		table.insert(ann_parts, "conjugation " .. base.conj)
		if base.irreg then
			table.insert(ann_parts, "irregular")
		end
	end
	table.insert(ann_parts,
		base.aspect == "impf" and "imperfective" or
		base.aspect == "pf" and "perfective" or
		"biaspectual")
	table.insert(ann_parts,
		base.trans == "tr" and "transitive" or
		base.trans == "intr" and "intransitive" or
		"reflexive")
	if base.impers then
		table.insert(ann_parts, "impersonal")
	end
	forms.annotation = " (" .. table.concat(ann_parts, ", ") .. ")"

	-- auxiliaries used in the table
	forms.nyama = link("ня́ма")
	forms.nyamashe = link("ня́маше")
	forms.nyamalo = link("ня́мало")
	forms.shte = link("ще")
	forms.shta = link("ща")
	forms.da = link("да")
	forms.bilo = link("било́")
	forms.sam = link("съм")
	forms.e = link("е")
	forms.bada = link("бъ́да")

	local aor_part_list_pers = [=[
	{paap_ind_m_sg_notr} {gm}, {paap_ind_f_sg_notr} {gf}, {paap_ind_n_sg_notr} {gn}, or {paap_ind_pl_notr} {gp}]=]
	local aor_part_list_impers = [=[
	{paap_ind_n_sg_notr}]=]
	local impf_part_list_pers = [=[
	{paip_m_sg_notr} {gm}, {paip_f_sg_notr} {gf}, {paip_n_sg_notr} {gn}, or {paip_pl_notr} {gp}]=]
	local impf_part_list_impers = [=[
	{paip_n_sg_notr}]=]

	local table_spec_non_compound = [=[
<div class="NavFrame">
<div class="NavHead" align=left>&nbsp; &nbsp; Conjugation of {lemma}{annotation}</div>
<div class="NavContent" align="center">

{\op}| style="background:#F0F0F0; font-size: 90%; width:100%; margin: 0 auto 0 auto; text-align:center;" class="inflection-table"
! colspan="2" style="width:10%; background:#e2e4c0;" | participles
! style="background:#e2e4c0" | present active participle
! style="background:#e2e4c0" | past active aorist participle
! style="background:#e2e4c0" | past active imperfect participle
! style="background:#e2e4c0" | past passive participle
! style="background:#e2e4c0" | verbal noun
! style="background:#e2e4c0" | adverbial participle
|-
! rowspan="3" style="background:#e2e4c0" | masculine
! style="background:#e2e4c0" | indefinite
|{prap_ind_m_sg}
|{paap_ind_m_sg}
|{paip_m_sg}
|{ppp_ind_m_sg}
| rowspan="5" |
| rowspan="9" |{advp}
|-
! style="background:#e2e4c0; white-space: nowrap;" | definite subject form
|{prap_def_sub_m_sg}
|{paap_def_sub_m_sg}
|—
|{ppp_def_sub_m_sg}
|-
! style="background:#e2e4c0; white-space: nowrap;" | definite object form
|{prap_def_obj_m_sg}
|{paap_def_obj_m_sg}
|—
|{ppp_def_obj_m_sg}
|-
! rowspan="2" style="background:#e2e4c0" | feminine
! style="background:#e2e4c0" | indefinite
|{prap_ind_f_sg}
|{paap_ind_f_sg}
|{paip_f_sg}
|{ppp_ind_f_sg}
|-
! style="background:#e2e4c0" | definite
|{prap_def_f_sg}
|{paap_def_f_sg}
|—
|{ppp_def_f_sg}
|-
! rowspan="2" style="background:#e2e4c0" | neuter
! style="background:#e2e4c0" | indefinite
|{prap_ind_n_sg}
|{paap_ind_n_sg}
|{paip_n_sg}
|{ppp_ind_n_sg}
|{vn_ind_sg}
|-
! style="background:#e2e4c0" | definite
|{prap_def_n_sg}
|{paap_def_n_sg}
|—
|{ppp_def_n_sg}
|{vn_def_sg}
|-
! rowspan="2" style="background:#e2e4c0" | plural
! style="background:#e2e4c0" | indefinite
|{prap_ind_pl}
|{paap_ind_pl}
|{paip_pl}
|{ppp_ind_pl}
|{vn_ind_pl}
|-
! style="background:#e2e4c0" | definite
|{prap_def_pl}
|{paap_def_pl}
|—
|{ppp_def_pl}
|{vn_def_pl}
|{\cl}
{\op}| style="background:#F0F0F0; font-size: 90%; width: 100%" class="inflection-table"
! colspan="{ncol}" rowspan="2" style="background:#C0C0C0" | person
! colspan="3" style="background:#C0C0C0" | singular
! colspan="3" style="background:#C0C0C0" | plural
|-
! style="background:#C0C0C0;width:12.5%" | first
! style="background:#C0C0C0;width:12.5%" | second
! style="background:#C0C0C0;width:12.5%" | third
! style="background:#C0C0C0;width:12.5%" | first
! style="background:#C0C0C0;width:12.5%" | second
! style="background:#C0C0C0;width:12.5%" | third
|-
! colspan="{ncol}" style="background:#c0cfe4" | indicative
! style="background:#c0cfe4" | аз
! style="background:#c0cfe4" | ти
! style="background:#c0cfe4" | той/тя/то
! style="background:#c0cfe4" | ние
! style="background:#c0cfe4" | вие
! style="background:#c0cfe4" | те
|-
! colspan="{ncol}" style="background:#c0cfe4" | present
| {pres_1sg}
| {pres_2sg}
| {pres_3sg}
| {pres_1pl}
| {pres_2pl}
| {pres_3pl}
|-
! colspan="{ncol}" style="background:#c0cfe4" | imperfect
| {impf_1sg}
| {impf_2sg}
| {impf_3sg}
| {impf_1pl}
| {impf_2pl}
| {impf_3pl}
|-
! colspan="{ncol}" style="background:#c0cfe4" | aorist
| {aor_1sg}
| {aor_2sg}
| {aor_3sg}
| {aor_1pl}
| {aor_2pl}
| {aor_3pl}
|-
]=]

	local table_spec_compound_short = [=[
! rowspan="2" style="background:#c0cfe4" | future
! style="background:#c0cfe4" | pos.
! colspan="6" style="background:#C0C0C0" | Use {shte} {refl} followed by the present indicative tense
|-
! style="background:#c0cfe4" | neg.
! colspan="6" style="background:#C0C0C0" | Use {nyama} {da} {refl} followed by the present indicative tense
|-
! rowspan= "2" style="background:#c0cfe4" | future in the past
! style="background:#c0cfe4" | pos.
! colspan="6" style="background:#C0C0C0" | Use the imperfect indicative tense of {shta} followed by {da} {refl} and the present indicative tense
|-
! style="background:#c0cfe4" | neg.
! colspan="6" style="background:#C0C0C0" | Use {nyamashe} {da} {refl} followed by the present indicative tense
|-
! colspan="2" style="background:#c0cfe4" | present perfect
! colspan="6" style="background:#C0C0C0" | Use the present indicative tense of {sam} {along_with_refl} and {aor_part_list}
|-
! colspan="2" style="background:#c0cfe4" | past perfect
! colspan="6" style="background:#C0C0C0" | Use the imperfect indicative tense of {sam} {along_with_refl} and {aor_part_list}
|-
! colspan="2" style="background:#c0cfe4" | future perfect
! colspan="6" style="background:#C0C0C0" | Use the future indicative tense of {sam} {along_with_refl} and {aor_part_list}
|-
! colspan="2" style="background:#c0cfe4" | future perfect in the past
! colspan="6" style="background:#C0C0C0" | Use the future in the past indicative tense of {sam} {along_with_refl} and {aor_part_list}
|-
! colspan="2" style="background:#c0e4c0" | renarrative
! style="background:#c0e4c0" | аз
! style="background:#c0e4c0" | ти
! style="background:#c0e4c0" | той/тя/то
! style="background:#c0e4c0" | ние
! style="background:#c0e4c0" | вие
! style="background:#c0e4c0" | те
|-
! colspan="2" style="background:#c0e4c0" | present and imperfect
! colspan="6" style="background:#C0C0C0; white-space: nowrap;" | Use the present indicative tense of {sam} (leave it out in third person) {along_with_refl} and {impf_part_list}
|-
! colspan="2" style="background:#c0e4c0" | aorist
! colspan="6" style="background:#C0C0C0" | Use the present indicative tense of {sam} (leave it out in third person) {along_with_refl} and {aor_part_list}
|-
! rowspan="2" style="background:#c0e4c0" | future and future in the past
! style="background:#c0e4c0" | pos.
! colspan="6" style="background:#C0C0C0" | Use the present/imperfect renarrative tense of {shta} followed by {da} {refl} and the present indicative tense
|-
! style="background:#c0e4c0" | neg.
! colspan="6" style="background:#C0C0C0" | Use {nyamalo} {da} {refl} and the present indicative tense
|-
! colspan="2" style="background:#c0e4c0" | present and past perfect
! colspan="6" style="background:#C0C0C0" | Use the present/imperfect renarrative tense of {sam} {along_with_refl} and {aor_part_list}
|-
! colspan="2" style="background:#c0e4c0" | future perfect and future perfect in the past
! colspan="6" style="background:#C0C0C0" | Use the future/future in the past renarrative tense of {sam} {along_with_refl} and {aor_part_list}
|-
! colspan="2" style="background:#f0e68c" | dubitative
! style="background:#f0e68c" | аз
! style="background:#f0e68c" | ти
! style="background:#f0e68c" | той/тя/то
! style="background:#f0e68c" | ние
! style="background:#f0e68c" | вие
! style="background:#f0e68c" | те
|-
! colspan="2" style="background:#f0e68c" | present and imperfect
! colspan="6" style="background:#C0C0C0" | Use the present/imperfect renarrative tense of {sam} {along_with_refl} and {impf_part_list}
|-
! colspan="2" style="background:#f0e68c" | aorist
! colspan="6" style="background:#C0C0C0" | Use the aorist renarrative tense of {sam} {along_with_refl} and {aor_part_list}
|-
! rowspan="2" style="background:#f0e68c" | future and future in the past
! style="background:#f0e68c" | pos.
! colspan="6" style="background:#C0C0C0" | Use the present/imperfect dubitative tense of {shta} followed by {da} {refl} and the present indicative tense
|-
! style="background:#f0e68c" | neg.
! colspan="6" style="background:#C0C0C0" | Use {nyamalo} {bilo} {da} {refl} and the present indicative tense
|-
! colspan="2" style="background:#f0e68c" | present and past perfect
| colspan="6" |<div class="center">''none''</div>
|-
! colspan="2" style="background:#f0e68c" | future perfect and future perfect in the past
! colspan="6" style="background:#C0C0C0; white-space: nowrap;" | Use the future/future in the past dubitative tense of {sam} {along_with_refl} and {aor_part_list}
|-
! colspan="2" style="background:#9be1ff" | conclusive
! style="background:#9be1ff" | аз
! style="background:#9be1ff" | ти
! style="background:#9be1ff" | той/тя/то
! style="background:#9be1ff" | ние
! style="background:#9be1ff" | вие
! style="background:#9be1ff" | те
|-
! colspan="2" style="background:#9be1ff" | present and imperfect
! colspan="6" style="background:#C0C0C0" | Use the present indicative tense of {sam} {along_with_refl} and {impf_part_list}
|-
! colspan="2" style="background:#9be1ff" | aorist
! colspan="6" style="background:#C0C0C0" | Use the present indicative tense of {sam} {along_with_refl} and {aor_part_list}
|-
! rowspan="2" style="background:#9be1ff" | future and future in the past
! style="background:#9be1ff" | pos.
! colspan="6" style="background:#C0C0C0" | Use the present/imperfect conclusive tense of {shta} followed by {da} {refl} and the present indicative tense
|-
! style="background:#9be1ff" | neg.
! colspan="6" style="background:#C0C0C0" | Use {nyamalo} {e} {da} {refl} and the present indicative tense
|-
! colspan="2" style="background:#9be1ff" | present and past perfect
! colspan="6" style="background:#C0C0C0" | Use the present/imperfect conclusive tense of {sam} {along_with_refl} and {aor_part_list}
|-
! colspan="2" style="background:#9be1ff" | future perfect and future perfect in the past
! colspan="6" style="background:#C0C0C0; white-space: nowrap;" | Use the future/future in the past conclusive tense of {sam} {along_with_refl} and {aor_part_list}
|-
! rowspan="2" colspan="2" style="background:#f2b6c3" | conditional
! style="background:#f2b6c3" | аз
! style="background:#f2b6c3" | ти
! style="background:#f2b6c3" | той/тя/то
! style="background:#f2b6c3" | ние
! style="background:#f2b6c3" | вие
! style="background:#f2b6c3" | те
|-
! colspan="6" style="background:#C0C0C0" | Use the first aorist indicative tense of {bada} {along_with_refl} and {aor_part_list}
|-
]=]

	local table_spec_end = [=[
! rowspan="2" colspan="{ncol}" style="background:#e4d4c0" | imperative
! style="background:#e4d4c0" | -
! style="background:#e4d4c0" | ти
! style="background:#e4d4c0" | -
! style="background:#e4d4c0" | -
! style="background:#e4d4c0" | вие
! style="background:#e4d4c0" | -
|-
|
| {impv_sg}
|
|
| {impv_pl}
|
|{\cl}{notes_clause}</div></div>]=]

	local notes_template = [===[
<div style="width:100%;text-align:left;background:#d9ebff">
<div style="display:inline-block;text-align:left;padding-left:1em;padding-right:1em">
{footnote}
</div></div>
]===]

	forms.aor_part_list = m_string_utilities.format(
		base.impers and aor_part_list_impers or aor_part_list_pers, forms)
	forms.impf_part_list = m_string_utilities.format(
		base.impers and impf_part_list_impers or impf_part_list_pers, forms)
	forms.notes_clause = forms.footnote ~= "" and
		m_string_utilities.format(notes_template, forms) or ""
	forms.ncol = fullmod and "3" or "2"
	local table_spec_compound = fullmod and fullmod.table_spec_compound_full or table_spec_compound_short
	local table_spec = table_spec_non_compound .. table_spec_compound .. table_spec_end
	return m_string_utilities.format(table_spec, forms)
end


-- Externally callable function to parse and decline a verb given user-specified arguments.
-- 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, pos, from_headword, def)
	local params = {
		[1] = {required = true, default = def or "пра́вя<2.1.impf.tr>"},
		footnote = {},
		title = {},
		full = {type = "boolean"},
	}
	if from_headword then
		params.lemma = {list = true}
		params.id = {}
		params.pos = {default = pos}
		params.cat = {list = true}
	end

	local args = m_para.process(parent_args, params)

	if args.title then
		track("overriding-title")
	end
	pos = args.pos or pos -- args.pos only set when from_headword

	local base, lemma = parse_word_spec(args[1])
	lemma = check_lemma_stress(base, lemma)
	lemma = detect_indicator_and_form_spec(base, lemma)
	conjs[base.conj](base, lemma)
	postprocess_base(base, lemma)
	base.forms = {}
	base.footnote = footnote
	conjugate_all(base)
	if base.refl then
		add_reflexive_suffix(base)
	end
	if base.impers then
		remove_non_impersonal_forms(base)
	end
	add_categories(base)
	local fullmod
	if args.full then
		fullmod = require("Module:bg-verb/full")
		fullmod.conjugate_all_compound(base)
	end
	base.forms.lemma = args.lemma and #args.lemma > 0 and args.lemma or
		{base.orig_lemma .. (base.refl and " " .. base.refl or "")}
	return base, fullmod
end


-- Main entry point. Template-callable function to parse and conjugate a verb given
-- user-specified arguments and generate a displayable table of the conjugated forms.
function export.show(frame)
	local parent_args = frame:getParent().args
	local base, fullmod = export.do_generate_forms(parent_args)
	show_forms(base, fullmod)
	return make_table(base, fullmod) .. require("Module:utilities").format_categories(base.categories, lang)
end


-- Template-callable function to parse and decline a noun given user-specified arguments and return
-- the forms as a string "SLOT=FORM,FORM,...|SLOT=FORM,FORM,...|...". Embedded pipe symbols (as might
-- occur in embedded links) are converted to <!>. If |include_props=1 is given, also include
-- additional properties (currently, only "|n=NUMBER"). This is for use by bots.
function export.generate_forms(frame)
	local include_props = frame.args["include_props"]
	local parent_args = frame:getParent().args
	local base = export.do_generate_forms(parent_args)
	return concat_forms(base, include_props)
end


return export