Module:User:Benwing2/cs-verb

local export = {}

--[=[

Authorship: Ben Wing

]=]

--[=[

TERMINOLOGY:

-- "slot" = A particular combination of tense/mood/person/number/gender/etc. Example slot names for verbs are "pres_1s" (present first singular) and "past_pasv_part_impers" (impersonal past passive participle). Each slot is filled with zero or more forms.

-- "form" = The conjugated Czech form representing the value of a given slot.

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

local lang = require("Module:languages").getByCode("cs") local m_table = require("Module:table") local m_links = require("Module:links") local m_string_utilities = require("Module:string utilities") local m_script_utilities = require("Module:script utilities") local iut = require("Module:User:Benwing2/inflection utilities") local m_para = require("Module:parameters") local com = require("Module:User:Benwing2/cs-common")

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

local u = mw.ustring.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 TEMP_REFLEXIVE_INSERTION_POINT = u(0xFFF0) -- temporary character used to mark the reflexive insertion point

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

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

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

local verb_slots = { ["infinitive"] = "inf", ["pres_act_part"] = "pres|act|part", ["past_act_part"] = "past|act|part", ["long_pass_part"] = "long|past|pass|part", ["pres_tgress_m"] = "m|s|pres|tgress", ["pres_tgress_fn"] = "f//n|s|pres|tgress", ["pres_tgress_p"] = "p|pres|tgress", ["past_tgress_m"] = "m|s|past|tgress", ["past_tgress_fn"] = "f//n|s|past|tgress", ["past_tgress_p"] = "p|past|tgress", ["vnoun"] = "vnoun", ["pres_fut_1s"] = "-", ["pres_fut_2s"] = "-", ["pres_fut_3s"] = "-", ["pres_fut_1p"] = "-", ["pres_fut_2p"] = "-", ["pres_fut_3p"] = "-", ["pres_1s"] = "1|s|pres|ind", ["pres_2s"] = "2|s|pres|ind", ["pres_3s"] = "3|s|pres|ind", ["pres_1p"] = "1|p|pres|ind", ["pres_2p"] = "2|p|pres|ind", ["pres_3p"] = "3|p|pres|ind", ["fut_1s"] = "1|s|fut|ind", ["fut_2s"] = "2|s|fut|ind", ["fut_3s"] = "3|s|fut|ind", ["fut_1p"] = "1|p|fut|ind", ["fut_2p"] = "2|p|fut|ind", ["fut_3p"] = "3|p|fut|ind", ["imp_2s"] = "2|s|imp", ["imp_1p"] = "1|p|imp", ["imp_2p"] = "2|p|imp", }

local function add_part_gender_number(pref, accel_template) for _, slot_accel_part in ipairs { {"m", "m|s"}, {"f", "f|s"}, {"n", "n|s"}, {"mp_an", "an|m|p"}, {"mp_in", "in|m|p"}, {"fp", "f|p"}, {"np", "n|p"}, } do		local slot, accel_part = unpack(slot_accel_part) verb_slots[pref .. "_" .. slot] = accel_template:format(accel_part) end end

-- List used for generating person-number-gender tenses like the past and conditional. Each element is a three-element -- list of {DEST_SUFFIX, GENDER_NUMBER_SUFFIX, TEMPLATE_IND} where DEST_SUFFIX is the suffix to add onto the destination -- prefix (e.g. "past_") to generate the slot; GENDER_NUMBER_SUFFIX is the suffix used to fetch the participle; and -- TEMPLATE_IND is the suffix used to fetch the appropriate person-number template. local person_number_gender_props = { {"1sm", "m", 1}, {"1sf", "f", 1}, {"1sn", "n", 1}, {"2sm", "m", 2}, {"2sf", "f", 2}, {"2sn", "n", 2}, {"3sm", "m", 3}, {"3sf", "f", 3}, {"3sn", "n", 3}, {"1pm", "mp_an", 4}, {"1pf", "fp", 4}, {"1pn", "np", 4}, {"2pm_polite", "m", 5}, {"2pm_plural", "mp_an", 5}, {"2pf_polite", "f", 5}, {"2pf_plural", "fp", 5}, {"2pn_polite", "n", 5}, {"2pn_plural", "np", 5}, {"3pm_an", "mp_an", 6}, {"3pm_in", "mp_in", 6}, {"3pf", "fp", 6}, {"3pn", "np", 6}, }

local function add_tense_person_number_gender(pref) for _, suffix_pair in ipairs(person_number_gender_props) do		local suffix, _, _ = unpack(suffix_pair) verb_slots[pref .. "_" .. suffix] = "-" end end

add_part_gender_number("lpart", "%s|l-part") add_part_gender_number("ppp", "short|%s|past|pass|part")

add_tense_person_number_gender("past") add_tense_person_number_gender("cond") -- Skip this as it's obsolete. -- add_tense_person_number_gender("past_perf") add_tense_person_number_gender("cond_past")

local budu_forms = { ["1s"] = "budu", ["2s"] = "budeš", ["3s"] = "bude", ["1p"] = "budeme", ["2p"] = "budete", ["3p"] = "budou", }

local override_stems = m_table.listToSet { "pres", "past", "imp", "ppp", "vn", "prtr", "patr", }

local function skip_slot(base, slot) if slot == "infinitive" then return false end if base.nopres and (rfind(slot, "pres") or rfind(slot, "fut")) then return true end if base.nopast and (rfind(slot, "past") or rfind(slot, "lpart")) then return true end if base.noimp and rfind(slot, "imp") then return true end if base.impers then -- Include _3s and _3sn slots, as well as _n and _fn slots for participles/transgressives. if rfind(slot, "3sn?$") or rfind(slot, "_f?n$") or slot == "vnoun" then return false else return true end end if (base.only3 or base.only3pl) and rfind(slot, "[12]") then return true end if (base.onlypl or base.only3pl) and (rfind(slot, "[123]s") or rfind(slot, "_[mfn]$") or rfind(slot, "_fn$")) then return true end if base.only3orpl and rfind(slot, "[12]s") then return true end return false end

local function add(base, slot, stems, endings, footnotes) if not endings then return end if skip_slot(base, slot) then return end endings = iut.combine_form_and_footnotes(endings, footnotes) local function combine_stem_ending(stem, ending) return com.combine_stem_ending(base, slot, stem, ending) end iut.add_forms(base.forms, slot, stems, endings, combine_stem_ending) end

local function map_forms(forms, fn) if type(forms) == "table" then forms = iut.convert_to_general_list_form(forms) for _, form in ipairs(forms) do			fn(form.form, form.footnotes) end else fn(forms) end end

local function fetch_footnotes(separated_group, allow_multiple_groups) local footnote_groups = allow_multiple_groups and {} or nil local footnotes for j = 2, #separated_group - 1, 2 do		if allow_multiple_groups and separated_group[j + 1] == ":" then table.insert(footnote_groups, footnotes or {}) elseif separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'") end if not footnotes then footnotes = {} end table.insert(footnotes, separated_group[j]) end if allow_multiple_groups then if not footnotes then error("Footnote-separating colon doesn't precede a footnote") end table.insert(footnote_groups, footnotes) return footnote_groups else return footnotes end end

-- No generate_default_pres1s_principal_part, which generates the pres_fut_1s principal part. -- This must be specified by each verb conjugation.

local function generate_default_pres3s_principal_part(base, do_err) return iut.map_forms(base.principal_part_forms.pres1s, function(form)		local stem = rmatch(form, "^(.*)[ui]$")		if stem then			-- There does not seem to be a case where ě can occur in the present third singular.			return stem .. "e"		end		stem = rmatch(form, "^(.*)m$") -- -ím or -ám		if stem then			return stem		end		error("'pres3s:' must be given in order to specify the present third-person singular principal part because " .. "first-person singular principal part '" .. form .. "' does not end in -u, -i or -m")	end) end

local function add_present_minus_1s_3p(base) local pres3s = base.principal_part_forms.pres3s add(base, "pres_fut_2s", pres3s, "š") add(base, "pres_fut_3s", pres3s, "") add(base, "pres_fut_1p", pres3s, "me") add(base, "pres_fut_2p", pres3s, "te") end

local function get_imptypes_for_stem(base, stem) local imptypes if rfind(stem, "[sš][tť]$") or rfind(stem, "tř$") then imptypes = {"long", "short"} else -- Substitute 'ch' with a single character to make the following code simpler. local modstem = stem:gsub("ch", com.TEMP_CH) if rfind(modstem, com.cons_c .. "[lr]" .. com.cons_c .. "$") then -- trp; not long imperative. imptypes = "short" elseif rfind(modstem, com.cons_c .. com.cons_c .. "$") then imptypes = "long" else imptypes = "short" end end

return imptypes end

local function get_imperative_principal_part_for_imptype(base, stem, imptype) local sg2, sg2_2 if imptype == "long" then return com.combine_stem_ending(base, "imp_2s", stem, "i") else -- See comment below at IV.1, there are rare cases where i-ě alternations occur in imperatives, e.g. -- podnítit impv. 'podniť ~ [rare] podněť'. 'ů' is reduced to 'u' rather than 'o', at least in -- půlit and derivatives. stem = com.apply_vowel_alternation(imptype == "short-ě" and "quant-ě" or "quant", stem, "noerror", "ring-u-to-u") if rfind(stem, com.velar_c .. "$") then return { {form = com.apply_first_palatalization(stem, "is verb")}, {form = com.apply_second_palatalization(stem, "is verb"), footnotes = "[obsolescent]"}, }		elseif rfind(stem, "[" .. com.paired_plain .. "]$") then return com.apply_second_palatalization(stem, "is verb") else return stem end end end

local function generate_default_imperative_principal_part_from_stem(base, stem) local imptypes = get_imptypes_for_stem(base, stem) return iut.flatmap_forms(iut.convert_to_general_list_form(imptypes), function(imptype)		return get_imperative_principal_part_for_imptype(base, stem, imptype)	end) end

local function generate_default_imperative_principal_part(base, do_err) return iut.flatmap_forms(base.forms.pres_fut_3p, function(form)		local stem = rmatch(form, "^(.*)ou$") or rmatch(form, "^(.*)í")		if not stem then			error("'imp:' must be given in order to specify the imperative principal part because third-person " .. "plural present/future '" .. form .. "' does not end in -ou or -í")		end		return generate_default_imperative_principal_part_from_stem(base, stem)	end) end

local function add_imperative(base, sg2, footnotes) add(base, "imp_2s", sg2, "", footnotes) -- "Long" imperatives end in -i local stem = rmatch(sg2, "^(.-)i$") if stem then add(base, "imp_1p", stem, "ěme", footnotes) add(base, "imp_2p", stem, "ěte", footnotes) elseif rfind(sg2, com.vowel_c .. "$") then error("Invalid 2sg imperative, ends in vowel other than -i: '" .. sg2 .. "'") else add(base, "imp_1p", sg2, "me", footnotes) add(base, "imp_2p", sg2, "te", footnotes) end end

local function add_pres_fut(base, stems, sg1, sg2, sg3, pl1, pl2, pl3, footnotes) add(base, "pres_fut_1s", stems, sg1, footnotes) add(base, "pres_fut_2s", stems, sg2, footnotes) add(base, "pres_fut_3s", stems, sg3, footnotes) add(base, "pres_fut_1p", stems, pl1, footnotes) add(base, "pres_fut_2p", stems, pl2, footnotes) add(base, "pres_fut_3p", stems, pl3, footnotes) end

local function generate_default_pres_tgress_principal_part(base, do_err) return iut.map_forms(base.forms.pres_fut_3p, function(form)		local pref = rmatch(form, "^(.*)ou$")		local ending		if pref then			ending = "a"		else			pref = rmatch(form, "^(.*)í")			if pref then				-- ě may be converted to e by com.combine_stem_ending				ending = "ě"			else				error("'prtr:' must be given in order to specify the present transgressive principal part because third-person " .. "plural present/future '" .. form .. "' does not end in -ou or -í")			end		end		return combine_stem_ending(base, "pres_tgress_m", pref, ending)	end) end

local function add_pres_tgress(base) add(base, "pres_tgress_m", base.principal_part_forms.prtr, "") local prtr_stems = iut.map_forms(base.principal_part_forms.prtr, function(form)		local pref = rmatch(form, "^(.*)a$")		if pref then			pref = pref .. "ou"		end		if not pref then			pref = rmatch(form, "^(.*)[eě]$")			if pref then				pref = pref .. "í"			end		end		if not pref then			error("Unrecognized present transgressive principal part '" .. form .. "', which does not end in -a, -e or -ě")		end		return pref	end) add(base, "pres_tgress_fn", prtr_stems, "c") add(base, "pres_tgress_p", prtr_stems, "ce") end

local function generate_default_pres_act_part_principal_part(base, do_err) return iut.map_forms(base.forms.pres_tgress_fn, function(form)		return form .. "í"	end) end

local function add_pres_act_part(base) add(base, "pres_act_part", base.principal_part_forms.pres_act_part, "") end

local function add_present_e(base, stems, pres1s3p_stems, soft_stem, prtr_endings, noimp, footnotes) pres1s3p_stems = base.pres1s3p_stems or pres1s3p_stems or stems stems = base.pres_stems or stems local s1_ending, p3_ending if soft_stem then s1_ending = "i" p3_ending = "í" else s1_ending = "u" p3_ending = "ou" end add_pres_fut(base, stems, nil, "eš", "e", "eme", "ete", nil, footnotes) add_pres_fut(base, pres1s3p_stems, s1_ending, nil, nil, nil, nil, p3_ending, footnotes) add_pres_tgress(base, stems, prtr_endings or p3_ending, footnotes) if not noimp then add_imperative_from_present(base, pres1s3p_stems, nil, footnotes) end end

local function add_present_a(base, stems, prtr_endings, noimp, footnotes) stems = base.pres_stems or stems add_pres_fut(base, stems, "ám", "áš", "á", "áme", "áte", "ají", footnotes) add_pres_tgress(base, stems, prtr_endings or "ají", footnotes) if not noimp then add_present_a_imperative(base, stems, footnotes) end end

local function add_present_i(base, stems, noimp) stems = base.pres_stems or stems add_pres_fut(base, stems, "ím", "íš", "í", "íme", "íte", "í") add_pres_tgress(base, stems, "í") if not noimp then add_imperative_from_present(base, stems) end end

local part_suffix_to_ending_list = { {"m", ""}, {"f", "a"}, {"n", "o"}, {"mp_an", "i"}, {"mp_in", "y"}, {"fp", "y"}, {"np", "a"}, }

local part_suffix_to_ending = {}

for _, suffix_ending in ipairs(part_suffix_to_ending_list) do	local suffix, ending = unpack(suffix_ending) part_suffix_to_ending[suffix] = ending end

-- No generate_default_past_principal_part, which generates the past_m principal part. -- This must be specified by each verb conjugation.

local function add_past_m(base) add(base, "lpart_m", base.principal_part_forms.past, "") end

local function generate_default_past_f_principal_part(base, do_err) iut.map_forms(base.principal_part_forms.past, function(form)		return form .. "a"	end) end

local function add_past_other_than_m(base) for _, suffix_ending in ipairs(part_suffix_to_ending_list) do		local suffix, ending = unpack(suffix_ending) if suffix ~= "m" then add(base, "lpart_" .. suffix, base.principal_part_forms.pastf, ending) end end end

local function generate_default_past_tgress_principal_part(base, do_err) return iut.map_forms(base.forms.lpart_m, function(form)		local pref = rmatch(form, "^(.*)l$")		if not pref then			error("'patr:' must be given in order to specify the past transgressive principal part because masculine " .. "singular l-participle '" .. form .. "' does not end in -l")		end		if rfind(pref, com.vowel_c .. "$") then			pref = pref .. "v"		end		return pref	end) end

local function add_past_tgress(base) -- Past transgressive not available for imperfective verbs. if base.aspect == "impf" then return end local patr_stems = base.principal_part_forms.patr add(base, "past_tgress_m", patr_stems, "") add(base, "past_tgress_fn", patr_stems, "ši") add(base, "past_tgress_p", patr_stems, "še") add(base, "past_act_part", patr_stems, "ší") end

-- No generate_default_ppp_principal_part, which generates the ppp_m principal part. -- This must be specified by each verb conjugation.

local function add_ppp(base) if base.ppp then for _, suffix_ending in ipairs(part_suffix_to_ending_list) do			local suffix, ending = unpack(suffix_ending) add(base, "ppp_" .. suffix, base.principal_part_forms.ppp, ending) end -- Add the long passive participle. Short participles in -án shorten to -an (e.g. 'jmenován' -> 'jmenovaný',		-- 'dělán' -> 'dělaný'). add(base, "long_past_part", iut.map_forms(base.forms.ppp_m, function(form) return form:gsub("án$", "an") end), "ý") end end

local function generate_default_vn_principal_part(base, do_err) -- Sometimes the vowel shortens; e.g. ptát ppp 'ptán' vn 'ptaní'; similarly hrát 'hraní', spát 'spaní', -- tkát 'tkaní', but not all- monosyllabic verbs, e.g. dbát ppp 'dbán' vn. 'dbání' or 'dbaní' -- and znát ppp. 'znán' vn. only 'znání'. These exceptional cases must be specified using overrides. return iut.map_forms(base.principal_part_forms.ppp, function(form)		return form .. "í"	end) end

local function add_vn(base) add(base, "vnoun", base.principal_part_forms.vn, "") end

--[=[ Data on how to conjugate individual rows (i.e. tense/aspect combinations, such as present indicative or conditional).

The order listed here matters. It determines the order of generating row forms. The order must have 'inf' < 'pres' < 'sub' < 'imp' < 'negimp' because the present indicative uses the root_stressed_stem generated by add_infinitive; the present subjunctive uses generated forms from the present indicative; the imperative uses forms from the present subjunctive and present indicative; and the negative imperative uses forms from the infinitive and the imperative. Similarly we must have 'fut' < 'cond' because the conditional uses the future principal part.

The following specs are allowed:

-- `desc` must be present and is an all-lowercase English description of the row. It is used in error messages and in  generating categories of the form 'Italian verbs with irregular ROW' and 'Italian verbs with missing ROW'. -- `tag_suffix` must be present is a string containing the tags that are appended onto the person/number tags to form the accelerator spec. For example, the spec "pres|sub" means that the accelerator spec for the third singular present subjunctive will be "3|s|pres|sub". This accelerator spec is passed to  Module:inflection utilities, which in turn passes it to Module:links when generating the link(s) for the corresponding verb form(s). The spec ultimately gets processed by Module:accel to generate the definition line for nonexistent verb forms. (FIXME: Accelerator support is currently disabled for forms with non-final accents.  We need to change the code in Module:inflection utilities so it sets the correct target not containing the   non-final accent.) -- `persnums` must be present and specifies the possible person/number suffixes to add onto the row-level slot (e.g. "phis" for the past historic) to form the individual person/number-specific slot (e.g. "phis2s" for the  second-person singular past historic). -- `row_override_persnums`, if present, specifies the person/number suffixes that are specified by a row override. If omitted, `persnums` is used. -- `row_override_persnums_to_full_persnums`, if present, specifies a mapping from the person/number suffixes specified by a row override to the person/number/suffixes used for conjugating the row. This is used, for example, with the subjunctive and imperfect subjunctive, where the first element of the row override specifies (respectively) the 123s and 12s forms, which need to be copied (respectively) to the 1s/2s/3s and 1s/3s forms. If omitted, no such copying happens. It's still possible for the row override persnums to disagree with the overall persnums. This happens, for example, with the imperative, where the 'improw:' row override spec specifies only the 2s and 2p forms; the remaining forms (3s, 1p, 3p) are generated during conjugation by copying from other forms, and can't be overridden using a row override. (They can still be overridden using a single override such  as 'imp3s:...' or a late single override such as 'imp3s!:...'. -- `generate_default_principal_part`, if present, should be a function of two arguments, `base` and `do_err`, and   should return the principal part(s) for the row. The return value can be anything that is convertible to the   "general list form" of a slot's forms, i.e. it can return a string, an object   {form = FORM, footnotes = {FOOTNOTE, FOOTNOTE, ...}}, or a list of either. It must be present if `conjugate` is   a table, but may be missing if `conjugate` is a function, in which case the function needs to generate the   principal part itself or otherwise handle things differently. For example, the present indicative does not   specify a value for `generate_default_principal_part` because there are actually two principal parts for the   present tense (first and third singular), which are processed at the beginning of the present indicative `conjugate` function. Similarly, the infinitive does not specify a value for `generate_default_principal_part` because there is no principal part to speak of; the infinitive is generated directly from the lemma in combination with the slash or backslash that follows the auxiliary and (in the case of a root-stressed infinitive) the single-vowel spec following the backslash. If `do_err` is given to this function, the function may throw an error if it can't generate the principal part; otherwise it should return nil. -- `conjugate` is either a function to conjugate the row (of two arguments, `base` and `rowslot`), or a table containing the endings to add onto the principal part to conjugate the row. In the latter case, there should be  the same number of elements in the table as there are elements in `row_override_persnums` (if given) or   `persnums` (otherwise). -- `no_explicit_principal_part` (DOCUMENT ME) -- `no_row_overrides` (DOCUMENT ME) -- `no_single_overrides` (DOCUMENT ME) -- `add_clitics` is a mandatory function of two arguments, `base` and `rowslot`, to add clitics to the forms for the specified row. It will only be called if `base.verb.linked_suf` is non-empty, i.e. there is a clitic to add. -- `dont_check_defective_status` (DOCUMENT ME) ]=] local row_conjugations = { {"inf", { desc = "infinitive", -- No generate_default_principal_part; handled specially in add_infinitive. conjugate = add_infinitive, no_explicit_principal_part = true, -- because handled specially using / or \ notation no_row_overrides = true, -- useless because there's only one form; use / or \ notation no_single_overrides = true, --useless because there's only one form; use / or \ notation add_clitics = add_infinitive_clitics, add_prefixed_reflexive_variants = add_non_finite_prefixed_reflexive_variants, }},	{"pres", { desc = "present indicative", conjugate = add_present_indic, -- No setting for no_explicit_principal_part here because it would never be checked; we special-case 'pres:' -- overrides before checking no_explicit_principal_part. The reason for special-casing is because there are two -- principal parts involved, "pres" and "pres3s", and we allow both to be specified using the syntax -- 'pres:PRES^PRES3S'. add_clitics = add_finite_clitics, }},	{"sub", { desc = "present subjunctive", generate_default_principal_part = generate_default_present_subj_principal_part, conjugate = add_present_subj, add_clitics = add_finite_clitics, }},	{"imp", { desc = "imperative", tag_suffix = "imp", persnums = imp_person_number_list, row_override_persnums = {"2s", "2p"}, generate_default_principal_part = generate_default_imperative_principal_part, conjugate = add_imperative, add_clitics = add_imperative_clitics, add_prefixed_reflexive_variants = add_imperative_prefixed_reflexive_variants, }},	{"past", { desc = "past", persnums = full_person_number_list, generate_default_principal_part = generate_default_past_historic_principal_part, conjugate = add_past_historic, add_clitics = add_finite_clitics, -- Set to "builtin" because normally handled specially in PRES^PRES3S,PHIS,PP spec, but when a built-in verb -- is involved, we want a way of overriding the past historic (using 'phis:'). no_explicit_principal_part = "builtin", }},	{"fut", { desc = "future", tag_suffix = "fut", persnums = full_person_number_list, generate_default_principal_part = generate_default_future_principal_part, conjugate = {"ò", "ài", "à", "émo", "éte", "ànno"}, add_clitics = add_finite_clitics, }},	{"cond", { desc = "conditional", tag_suffix = "cond", persnums = full_person_number_list, generate_default_principal_part = generate_default_conditional_principal_part, conjugate = {"èi", "ésti", {"èbbe", "ébbe"}, "émmo", "éste", {"èbbero", "ébbero"}}, add_clitics = add_finite_clitics, }},	{"pp", { desc = "past participle", tag_suffix = "past|part", persnums = {""}, generate_default_principal_part = generate_default_past_participle_principal_part, conjugate = {""}, add_clitics = add_participle_clitics, -- Set to "builtin" because normally handled specially in PRES^PRES3S,PHIS,PP spec, but when a built-in verb -- is involved, we want a way of overriding the past participle (using 'pp:'). no_explicit_principal_part = "builtin", no_row_overrides = true, -- useless because there's only one form; use the PRES^PRES3S,PHIS,PP or pp: spec no_single_overrides = true, --useless because there's only one form; use the PRES^PRES3S,PHIS,PP or pp: spec }},	{"ger", { desc = "gerund", tag_suffix = "ger", persnums = {""}, generate_default_principal_part = generate_default_gerund_principal_part, conjugate = {""}, add_clitics = add_gerund_clitics, add_prefixed_reflexive_variants = add_non_finite_prefixed_reflexive_variants, no_row_overrides = true, -- useless because there's only one form; use explicit principal part no_single_overrides = true, -- useless because there's only one form; use explicit principal part }},	{"presp", { desc = "present participle", tag_suffix = "pres|part", persnums = {""}, generate_default_principal_part = generate_default_present_participle_principal_part, conjugate = {""}, add_clitics = add_participle_clitics, no_row_overrides = true, -- useless because there's only one form; use explicit principal part no_single_overrides = true, -- useless because there's only one form; use explicit principal part -- Disable this; seems most verbs do have present participles -- not_defaulted = true, -- not defaulted, user has to request it explicitly dont_check_defective_status = true, -- this is frequently missing and doesn't indicate a defective verb }}, }

local row_conjugation_map = {}

for _, rowconj in ipairs(row_conjugations) do	local rowslot, rowspec = unpack(rowconj) row_conjugation_map[rowslot] = rowspec end

local function separate_stem_suffix(lemma, regex, class) local stem, suffix = rmatch(lemma, regex) if not stem and class then error("Unrecognized lemma for class " .. class .. ": '" .. lemma .. "'") end return stem, suffix end

local function parse_variant_codes(run, allowed_codes, variant_type, parse_err) local allowed_code_set = m_table.listToSet(allowed_codes) local colon_separated_groups = iut.split_alternating_runs_and_strip_spaces(run, ":") local retval = {} for _, group in ipairs(colon_separated_groups) do		for i, code in ipairs(allowed_codes) do allowed_codes[i] = "'" .. code .. "'"		end if not allowed_code_set[group[1]] then parse_err(("Unrecognized variant code '%s' for %s: should be one of %s"):format(group[1], variant_type, m_table.serialCommaJoin(allowed_codes))) end table.insert(retval, {form = group[1], footnotes = fetch_footnotes(group)}) end return retval end

local parse = {} local conjs = {}

--[=[ Verbs whose infinitive and present stems both end in a consonant. 1. Verbs in -k/h: [péct]] "to bake": pres. 'peču' (very bookish/outdated 'peku', not in IJP), 'pečeš', 'peče', 'pečeme', 'pečete', 'pečou' (very bookish/outdated pekou) síct "to sow": pres. 'seču', etc. like péct téct "to flow": pres. 'teču', etc. like péct tlouct "to beat": pres. 'tluču', etc. like péct vléct "to drag": pres. 'vleču', etc. like péct moct "to be able": pres. 'mohu' (colloquial 'můžu'), 'můžeš', ..., 'můžete', 'mohou' (colloquial 'můžou') stříci se "to beware" (bookish, becoming obsolete except the imperative): pres. 'střehu se', 'střežeš se', ..., 'střežete se', 'střehou se' These verbs have alternative bookish/obsolescent infinitives in '-ci', although 'moci' is still common and actually more frequent. Note also derivatives, e.g. přemoct "to overpower", pomoct "to help", obléct "to put on" (= ob- + vléct). ]=]

--[=[ conjs["I.1"] = function(base, lemma) local suffix local stem = separate_stem_suffix(lemma, "^(.*)c[ti]$") if stem then if not base.cons or (base.cons ~= "k" and base.cons ~= "h") then error("Must specify consonant as 'k' or 'h' for verbs in -ct/-ci") end end if not stem then stem, suffix = separate_stem_suffix(lemma, "^(.*)([bp])sti?$") if stem then -- zábsti (pres1s 'zebu'); obsolete hřébsti (hřebu), skúbsti (skubu), dlúbsti (dlubu), -- tépsti (tepu) local presstem = com.apply_vowel_alternation(base.ye and "quant-ě" or "quant", stem) add_present_e(base, presstem

if last_cons == "к" or last_cons == "г" then error("Use class 8 for lemmas in -гти and -кти: '" .. lemma .. "'") end if last_cons == "р" then error("Use class 9 for lemmas in -рти: '" .. lemma .. "'") end if last_cons == "с" then if base.cons then last_cons = base.cons else error("With lemmas in -сти, must specify final consonant: '" .. lemma .. "'") end elseif base.cons then error("Can only specify final consonant '" .. base.cons .. "' with lemma ending in -сти: '" .. lemma .. "'") end local stressed_stem = com.maybe_stress_final_syllable(stem) add_present_e(base, stressed_stem .. last_cons) local past_msg, past_rest if base.cons == "д" or base.cons == "т" or base.cons == "в" then -- NOTE: This applies to плисти́ (with base.cons == "в") but not пливти́ past_msg = stressed_stem .. "в" past_rest = stressed_stem .. "л" elseif base.cons == "ст" then past_msg = stressed_stem .. "с" past_rest = past_msg .. "л" else past_msg = stressed_stem .. last_cons past_rest = past_msg .. "л" end if base.i then past_msg = rsub(past_msg, "[ео](́?" .. com.cons_c .. "+)$", "і%1") end add_past(base, past_msg, past_rest) add_ppp(base, stressed_stem .. last_cons .. "ен") end ]=]

--[=[ II.1 (stem ends in a consonant):

E.g.

blbnout past^(nu) (blbl ~ blbnul, blbnut, impf, vn blbnutí) plácnout past^(nu) (plácl ~ plácnul, plácnut, plácnuv, vn plácnutí) padnout (pf.) patr^nu^- (padl, no PPP, padnuv ~ pad, padnutí) padnout (biasp.) patr^nu^- (padl, no PPP, padnuv ~ pad, padnutí) napadnout ppp^n.vn^n^t.patr^nu^- (napadl, napaden, napadnuv ~ napad, napadení ~ napadnutí) odpadnout patr^-^nu (odpadl, no PPP, odpad ~ odpadnuv, odpadnutí) přepadnout ppp^n.vn^n^t.patr^-^nu (přepadl, přepaden, přepad ~ přepadnuv, přepadení ~ přepadnutí) rozpadnout se patr^nu^- (rozpadl se, rozpadnut, rozpadnuv se ~ rozpad se, rozpadnutí se) dopadnout ppp^n.vn^n^t.patr^nu^- (dopadl, dopaden, dopadnuv ~ dopad, dopadení ~ dopadnutí [VN differentiated by meaning: dopadení [přistižení = "catch?"] vs. dopadnutí [padnutí = "fall?"]) spadnout ppp^n.vn^t.patr^nu^- (spadl, spaden, spadnuv ~ spad, vn spadnutí) upadnout ppp^n^t.vn^t.patr^-^nu (upadl, upaden ~ upadnut, upad ~ upadnuv, vn upadnutí) vypadnout patr^-^nu (vypadl, no PPP, vypad ~ vypadnuv, vn vypadnutí) ukradnout - (ukradl, ukradnut, ukradnuv, ukradnutí) [but IJP commentary says 'ukraden' and 'ukradení' still in use] chřadnout - (chřadl, no PPP, impf, chřadnutí) blednout - (bledl, no PPP, impv, blednutí) sednout - (sedl, no PPP, sednuv, sednutí) usednout - (usedl, usednut, usednuv, usednutí) nastydnout - (nastydl, nastydnut, nastydnuv, nastydnutí) sládnout - (sládl, no PPP, impf, sládnutí) vládnout - (vladl, no PPP, impf, vn vládnutí) zvládnout - (zvládl, zvládnut, zvládnuv, vn zvládnutí) rafnout past^(nu) (rafl ~ rafnul, rafnut, rafnuv, vn rafnutí) hnout past^nu (hnul, hnut, hnuv, hnutí), same for pohnout nadchnout past^(nu).ppp^t^n (nadchl ~ nadchnul, nadchnut ~ nadšen, nadchnuv, nadchnutí ~ nadšení) schnout - (schnul, schnut, impf, vn schnutí) uschnout past^(nu) (uschl ~ uschnul, uschnut, uschnuv, vn uschnutí) vyschnout past^(nu) (vyschl ~ vyschnul, no PPP, vyschnuv, vn vyschnutí) bouchnout past^(nu) (bouchl ~ bouchnul, bouchnut, bouchnuv, vn bouchnutí) oblehnout ppp^n (oblehl, obležen, oblehnuv, obležení) stihnout ppp^n.vn^t^n (stihl, stižen, stihnuv, stihnutí ~ stižení) zastihnout ppp^n (zastihl, zastižen, zastihnuv, zastižení) zdvihnout ppp^n.vn^t^n (zdvihl, zdvižen, zdvihnuv, vn zdvihnutí ~ zdvižení) pozdvihnout ppp^n (pozdvihl, pozdvižen, pozdvihnuv, vn pozdvižení) střihnout ppp^n.vn^t^n (střihl, střižen, střihnuv, střihnutí ~ střižení) ustřihnout ppp^n.vn^t^n (ustřihl, ustřižen, ustřihnuv, ustřihnutí ~ ustřižení) trhnout past^(nu).ppp^t^n (trhl ~ trhnul, trhnut ~ tržen, trhnuv, trhnutí ~ tržení) podtrhnout past^(nu).ppp^n.vn^t or past^(nu).ppp^n [by meaning] (podtrhl ~ podtrhnul, podtržen, podtrhnuv, podtrhnutí ~ podtržení [VN differentiated by meaning: podtrhnutí [of a chair], podtržení [of words]]) roztrhnout ppp^n (roztrhl, roztržen, roztrhnuv, roztržení) vrhnout past^(nu).ppp^t^n (vrhl ~ vrhnul, vrhnut ~ vržen, vrhnuv, vn vrhnutí ~ vržení) svrhnout ppp^n (svrhl, svržen, svrhnuv, svržení) vyvrhnout past^(nu) or past^(nu).ppp^n [by meaning] (vyvhrl ~ vyvrhnul, vyvrhnut ~ vyvržen [PPP differentiated by meaning: 'vyvržen' [ejected (from society)], 'vyvrhnut' [published]], vyvrhnuv, vyvrhnutí ~ vyvržení) sáhnout - (sáhl, no PPP, sáhnuv, sáhnutí) zasáhnout ppp^n.vn^t^n (zasáhl, zasažen, zasáhnuv, zasáhnuti ~ zasažení) obsáhnout - or ppp^n [by meaning] (obsáhl, obsáhnut ~ obsažen [differentiated by meaning], obsáhnuv, obsáhnutí ~ obsažení) přesáhnout ppp^n.vn^t (přesáhl, přesažen, přesáhnuv, přesáhnutí) dosáhnout ppp^n (dosáhl, dosažen, dosáhnuv, dosažení) táhnout ppp^n (táhl, tažen, impv, tažení) zatáhnout ppp^n (zatáhl, zatažen, zatáhnuv, zatažení) vytáhnout ppp^n (vytáhl, vytažen, vytáhnuv, vytažení) napřáhnout ppp^n.vn^t (napřáhl, napřažen, napřáhnuv, napřáhnutí) přeřeknout se ppp^n^t.vn^t (přeřekl se, přeřečen ~ přeřeknut, přeřeknuv se, přeřeknutí se) křiknout past^(nu) (křikl ~ křiknul, no PPP, křiknuv, vn křiknutí) polknout past^(nu) (polkl ~ polknul, polknut, polknuv, vn polknutí) zamknout ppp^n^t (zamkl, zamčen ~ zamknut, zamknuv, zamčení ~ zamknutí) obemknout - (obemkl, obemknut, obemknuv, obemknutí) odemknout ppp^n^t (odemkl, odemčen ~ odemknut, odemknuv, odemčení ~ odemknutí) semknout patr^- (semkl, semknut, semk, semknutí) přimknout - (přimkl, přimknut, přimknuv, přimknutí) vymknout - (vymkl, vymknut, vymknuv, vymknutí) cinknout past^(nu) (cinkl ~ cinknul, cinknut, cinknuv, vn cinknutí) fnrknout past^(nu) (fnrkl ~ fnrknul, no PPP, fnrknuv, fnrknutí) prasknout past^(nu) (praskl ~ prasknul, prasknut, prasknuv, vn prasknutí) tisknout - or ppp^n [by meaning] (tiskl, tisknut ~ tištěn [differentiated by meaning], impf, tisknutí ~ tištění) stisknout - (stiskl, stisknut, stisknuv, stisknutí) vytisknout past^(nu).ppp^t^n (vytiskl ~ vytisknul, vytisknut ~ vytištěn, vytisknuv, vytisknutí ~ vytištění) blýsknout past^(nu) (blýskl ~ blýsknul, no PPP, blýsknuv, blýsknutí) tknout se - (tknul se, tknut [PPP with reflexive], tknuv se, vn tknutí) dotknout se past^(nu).ppp^n^t.vn^t (dotkl se ~ dotknul se, dotčen ~ dotknut [PPP with reflexive], dotknuv se, vn dotknutí) vytknout past^(nu).ppp^n^t (vytkl ~ vytknul, vytčen ~ vytknut, vytknuv, vn vytčení ~ vytknutí); same for protknout, zatknout kouknout past^(nu) (koukl ~ kouknul, no PPP, kouknuv, kouknutí) nařknout ppp^n^t (nařkl, nařčen ~ nařknut, nařknuv, nařčení ~ nařknutí) přiřknout ppp^n^t (přiřkl, přiřčen ~ přiřknut, přiřknuv, přiřčení ~ přiřknutí) uřknout - (uřkl, uřknut, uřknuv, uřknutí) vyřknout ppp^n^t (vyřkl, vyřčen ~ vyřknut, vyřknuv, vyřčení ~ vyřknutí) obléknout [also oblíknout ~ obléct ~ obléci ~ oblíct] ppp^n^t (oblékl, oblečen ~ obléknut, obléknuv, oblečení ~ obléknutí [VN differentiated by meaning: oblečení [of clothing], obléknutí]) vléknout [obsolete for vléct per IJP] navléknout [also navlíknout ~ navléct ~ navléci ~ navlíct] ppp^n^t.vn^t (navlékl, navlečen ~ navléknut, navléknuv, navléknutí) převléknout [also převlíknout ~ převléct ~ převléci ~ převlíct] ?? ppp^n^t.vn^n vs. ppp^n^t.vn^t [by meaning; unless PPP is also distinguished by meaning] (převlékl ~ převléknul, převlečen ~ převléknut, převléknuv, převlečení ~ převléknutí [VN differentiated by meaning: převlečení [disguise] ~ převléknutí [change clothing]]) svléknout [also svlíknout ~ svléct ~ svléci ~ svlíct] past^(nu).ppp^n^t (svlékl ~ svléknul, svlečen ~ svléknut, svléknuv, svlečení ~ svléknutí) oblíknout - (oblíkl, oblíknut, oblíknuv, oblíknutí) lnout past^nu (lnul, no PPP, impf, lnutí) přilnout past^nu (přilnul, přilnut, přilnuv, přilnutí) povšimnout si - (povšiml si, povšimnut si, povšimnuv si, povšimnutí) klapnout past^(nu) (klapl ~ klapnul, klapnut, klapnuv, vn klapnutí) klepnout past^(nu) (klepl ~ klepnul, klepnut, klepnuv, vn klepnutí) dupnout past^(nu) (dupl ~ dupnul, no PPP, dupnuv, vn dupnutí) vyhoupnout se past^(nu) (vyhoupl se ~ vyhoupnul se, vyhoupnut [WHAT DOES A REFLEXIVE VERB WITH PPP MEAN?], vyhoupnuv se, vyhoupnutí) křupnout past^(nu) (křupl ~ křupnul, křupnut, křupnuv, křupnutí) stárnout - (stárl, no PPP, impf, stárnutí) užasnout - (užasl, no PPP, užasnuv, užasnutí) zesnout past^nu (zesnul, no PPP, zesnuv, zesnutí) smlsnout past^(nu) (smlsl ~ smlsnul, no PPP, impf, vn smlsnutí) usnout past^nu (usnul, usnut, usnuv, usnutí) bohatnout - (bohatl, no PPP, impf, bohatnutí) procitnout - (procitl, no PPP, procitnuv, procitnutí) zhltnout past^(nu) (zhltl ~ zhltnul, zhltnut, zhltnuv, vn zhltnutí) škrtnout past^(nu) (šrktl ~ šrktnul, škrtnout, šrktnuv, vn šrktnutí) zvrtnout past^(nu) (zvrtl ~ zvrtnul, zvrtnut, zvrtnuv, vn zvrtnutí) couvnout past^(nu) (couvl ~ couvnul, no PPP, couvnuv, vn couvnutí) naleznout [not in IJP] [but IJP commentary says 'nalezen' and 'nalezení' still in use] vynaleznout [not in IJP] [but IJP commentary says 'vynalezen' and 'vynalezení' still in use] mrznout past^(nu) (mrzl ~ mrznul, mrznut, impf, vn mrznutí) uváznout past^(nu) (uvázl ~ uváznul, no PPP, uváznuv, uváznutí) říznout past^(nu) (řízl ~ říznul, říznut, říznuv, říznutí), same for vyříznout, doříznout, naříznout, rozříznout)

Per IJP, the past transgressive always ends in -nuv, and the endingless forms are totally obsolete. (But not always it seems, see above.)

Variation:

Past: -l, -nul, -l ~ -nul: - nu (nu) [must be specified] PPP: -nut, -en, -nut ~ -en, -en ~ -nut: t n t:n n:t [defaults to t] past tgress: -nuv, null, -nuv ~ null, null ~ -nuv: nu - nu:- -:nu [defaults to nu] vn: -nutí, -ení, -nutí ~ -ení, -ení ~ -nutí: defaults to same as PPP, or t if no PPP ]=]

conj["II.1"] = { get_infstem = function(base) return separate_stem_suffix(lemma, "^(.*)nout$", "II.1") end, pres1s = "n", imp = "ni", ppp = { choices = {"l", "nul", "(nu)"}, default = "iot", generate_part = function(base, variant) if variant == "iot" then local iotated_stem = com.iotate(base.infstem) return com.combine_stem_ending(base, "ppp_m", iotated_stem, "en") elseif variant == "ni" then return com.combine_stem_ending(base, "ppp_m", base.infstem, "en") else error("Internal error: Saw unrecognized PPP variant code '" .. variant .. "'") end end, },	past = "il", ppp = { choices = {"iot", "ni"}, default = "iot", generate_part = function(base, variant) if variant == "iot" then local iotated_stem = com.iotate(base.infstem) return com.combine_stem_ending(base, "ppp_m", iotated_stem, "en") elseif variant == "ni" then return com.combine_stem_ending(base, "ppp_m", base.infstem, "en") else error("Internal error: Saw unrecognized PPP variant code '" .. variant .. "'") end end, }, }

parse["II.1"] = function(base, conjmod_run, parse_err) local separated_groups = iut.split_alternating_runs_and_strip_spaces(conjmod_run, "[/,]", "preserve splitchar") local past_conjmod = separated_groups[1] local ppp_conjmod, vn_conjmod, ptr_conjmod local expected_ptr_index = 3 if #separated_groups > 1 and separated_groups[2][1] == "/" then ppp_conjmod = separated_groups[3] expected_ptr_index = 5 if #separated_groups > 3 and separated_groups[4][1] == "/" then vn_conjmod = separated_groups[5] expected_ptr_index = 7 end end if #separated_groups >= expected_ptr_index then local ptr_separator = separated_groups[expected_ptr_index - 1][1] if ptr_separator ~= "," then parse_err("Expected a comma as separator for past transgressive but saw '" .. ptr_separator .. "'") end ptr_conjmod = separated_groups[expected_ptr_index] if #separated_groups > expected_ptr_index then parse_err("Junk at end of II.1 conjugation modifier spec, after past transgressive spec") end end

base.past_stem = parse_variant_codes(past_conjmod, {"-", "nu", "(nu)"}, "II.1 past tense", parse_err) if not ppp_conjmod then base.ppp_stem = else base.ppp_stem = parse_variant_codes(ppp_conjmod, {"", "t", "n"}, "II.1 past passive participle", parse_err) end if not vn_conjmod then base.vn_stem = else base.vn_stem = parse_variant_codes(ppp_conjmod, {"", "t", "n"}, "II.1 verbal noun", parse_err) end if not vn_conjmod then base.ptr_stem = else base.ptr_stem = parse_variant_codes(ppp_conjmod, {"-", "nu"}, "II.1 past transgressive", parse_err) end end

conjs["II.1"] = function(base, lemma) local stem = separate_stem_suffix(lemma, "^(.*)nout$", "II.1")

local function shorten_and_palatalize(stem) local begin, lastv, lastc = rmatch(stem, "^(.*)(" .. com.vowel_c .. ")(" .. com.cons_c .. "*)$")		if not begin then begin = "" lastv = "" lastc = stem elseif lastv == "á" then -- vytáhnout -> 'vytažen' lastv = "a" elseif lastv == "é" then -- obléknout -> 'oblečen' -- FIXME, do we need to convert ň/ť/ď to plain versions and vowel to ě? lastv = "e" end -- tisknout -> 'tištěn' -- nadchnout -> 'nadšen' -- práhnout -> 'pražen' -- vléknout -> 'vlečen' lastc = com.apply_first_palatalization(lastc, "verb") return begin .. lastv .. lastc end

-- Normalize the codes computed by the parse function above.

-- First the past tense. We need to expand "(nu)" to "-" and "nu", then normalize appropriately. local saw_paren_nu = false for _, formobj in ipairs(base.past_stem) do		if formobj.form == "(nu)" then saw_paren_nu = true break end end local expanded_past if saw_paren_nu then expanded_past = {} for _, formobj in ipairs(base.past_stem) do			if formobj.form == "(nu)" then table.insert(expanded_past, {form = "-", footnotes = formobj.footnotes}) table.insert(expanded_past, {form = "nu", footnotes = formobj.footnotes}) else table.insert(expanded_past, formobj) end end else expanded_past = base.past_stem end for _, formobj in ipairs(expanded_past) do		if formobj.form == "-" then formobj.form = stem elseif formobj.form == "nu" then formobj.form = stem .. "nu" else error("Internal error: Saw unrecognized past tense variant code '" .. formobj.form .. "'") end end

local function normalize_ppp_and_vn(stems, stem_type) for _, formobj in ipairs(stems) do			if formobj.form == "t" then formobj.form = stem .. "nut" elseif formobj.form == "n" then -- We need to call combine_stem_ending in case of e.g. tisknout -> stem 'tišť' -> 'tištěn'. formobj.form = com.combine_stem_ending(base, nil, shorten_and_palatalize(stem), "en") else error("Internal error: Saw unrecognized " .. stem_type .. " variant code '" .. formobj.form .. "'") end end end

-- Now the PPP and verbal noun. normalize_ppp_and_vn(base.ppp_stem, "PPP") normalize_ppp_and_vn(base.vn_stem, "verbal noun")

-- Now the past transgressive. for _, formobj in ipairs(base.ptr_stem) do		if formobj.form == "-" then formobj.form = stem elseif formobj.form == "nu" then formobj.form = stem .. "nuv" else error("Internal error: Saw unrecognized past transgressive variant code '" .. formobj.form .. "'") end end

add_present_e(base, stem .. "n") -- Masculine singular past may have -l, -nul or both, but other forms of the past generally only have -l. add_past(base, expanded_past, stem, base.ptr_stem) add_ppp(base, base.ppp_stem, base.vn_stem) end

--[=[ E.g.:

krýt (kryji ~ kryju [colloquial], kryl, kryt, kryje, krytí) zakrýt (zakryji ~ zakryju [colloquial], zakryl, zakryt, zakryv, zakrytí) mýt, nýt, rýt, týt, výt (essentially like 'krýt') pít (piji ~ piju [colloquial], pil, pit, impf, pití) vypít (vypiji ~ vypiju [colloquial], vypil, vypit, vypiv, vypití) bít (biji ~ etc., bil, bit, impf, bití) rozbít (rozbiji, rozbil, rozbit, rozbiv, rozbití) šít (šiji, šil, šit, impf, šití) sešít (sešiji, sešil, sešit, sešiv, sešití) [not in IJP] žít (žiji, žil, žit, impf, žití) prožít (prožiji, prožil, prožit, proživ, prožití) klít (kleji, klel, klet, impf, kletí ~ klení) proklít (prokleji, proklel, proklet, proklev, prokletí) dout (duji, dul, no PPP, impf, dutí) zadout (zaduji, zadul, no PPP, zaduv, zadutí) lát (laji [impv laj, prtg laje], lál, lán, impf, lání) vylát (vylaji [impv vylaj], vylál, vylán, vyláv, vylání) [not in IJP] tát (taji [impv taj, prtg taje], tál, no PPP [given as tán (tát) in Wikipedia], impf, tání) roztát (roztaji [impv roztaj, prtg roztaje], roztál, roztán, roztáv, roztání) přát (přeji [impv přej, prtg přeje], přál, no PPP, impf, přání) popřát (popřeji [impv popřej], popřál, popřán, popřáv, popřání) smát se (směji se [impv směj se, prtg směje se], smál se, no PPP [given as smán (smát) in Wikipedia], impf, no VN [given as smání (smátí) in Wikipedia]) usmát se (usměji se [impv usměj se], usmál se, no PPP, usmáv [NOTE: IJP has usmav but this appears to be a mistake], usmání) nasmát se [biaspectual: also prtg 'nasměje se'] (nasměji se [impv nasměj se], nasmál se, no PPP, nasmáv, nasmání) sít (seji [impv sej, prtg seje], sel ~ sil, set, impf, setí) zasít (zaseji [impv zasej], zasel ~ zasil, zaset, zasev, zasetí) vát (věji [impv věj, prtg věje], vál, no PPP [given as ván (vát) in Wikipedia], impf, vání [vátí is very rare]) navát (navěji [impv navěj, prtg navěje], navál, navát, naváv, navátí) hrát (hraji [impv hraj ~ hrej [bookish]], hrál, hrán, hraje, hraní [NOTE: short vowel])

Variation:

(za)set. Similarly for past transgressive.
 * Present tense shortens infinitive vowel: ý -> y; ou -> u; í -> i or ě; á -> a or ě.
 * Past tense vowel matches present vowel unless infinitive has á, in which case past tense has á.
 * PPP has -t unless infinitive has á, in which case PPP may have -n or -t (or maybe both).
 * PPP stem usually matches past stem; but note sít, zasít with past stem '(za)sel ~ (za)sil' but PPP only
 * No instances I can see of verbal noun stem disagreeing with PPP stem.

Variant codes: - Use 'n' for PPP in -n, 't' for PPP in '-t', 'n:t' or 't:n' for both (defaults to 'n'):
 * lát, vylát: III.1
 * tát, roztát: III.1
 * přát, popřát: III.1.pres^ě
 * smát se: III.1.pres^ě.vn:-
 * usmát se, nasmát se: III.1.ppp^n.ě
 * vát: III.1.ppp^n^t[rare]
 * navát: III.1.ppp^t

]=] parse["III.1"] = function(base, conjmod_run, parse_err) base.ppp_stem = parse_variant_codes(conjmod_run, {"", "t", "n"}, "III.1 past passive participle", parse_err) end

conjs["III.1"] = function(base, lemma) local stem = separate_stem_suffix(lemma, "^(.*)t$", "III.1") local begin, lastv = rmatch(stem, "^(.*)(ou)$") if not begin then begin, lastv = rmatch(stem, "^(.*)([áíý])$") end if not begin then error("Unrecognized lemma for class III.1: '" .. lemma .. "' (should end in -át, -ít, -ýt or -out)") end local pres_stem = com.apply_vowel_alternation(base.ye and "quant-ě" or "quant", stem) local past_stem = lastv == "á" and stem or pres_stem

-- Normalize the codes computed by the parse function above. if not base.ppp_stem then base.ppp_stem = end for _, formobj in ipairs(base.ppp_stem) do formobj.form = past_stem .. formobj.form end

add_present_e(base, pres_stem .. "j", nil, "soft") add_present_e(base, {}, pres_stem .. "j", false, {}, "noimp", "[colloquial]") add_past(base, past_stem) add_ppp(base, base.ppp_stem) end

--[=[ III.2:

darovat "to donate" sledovat "to follow" konstruovat "to construct" ]=] conjs["III.2"] = { get_infstem = function(base) return separate_stem_suffix(base.lemma, "^(.*)ovat$", "III.2") end, pres1s = {{form = "uji"}, {form = "uju", footnotes = {"[colloquial]"}}}, prtr = "ě", past = "oval", ppp = "ován", }

--[=[ IV.1:

prosit (prosím, pros, prosil, prošen, prose, prošení) vyprosit (vyprosím, vypros, vyprosil, vyprošen, vyprosiv, vyprošení) nosit (nosím, nos, nosil, nošen, nose, nošení) nanosit (nanosím, nanos, nanosil, nanošen, nanosiv, nanošení) spasit (spasím, spas, spasil, spasen, spasiv, spasení) cizopasit (cizopasím, cizopas, cizopasil, cizopasen, cizopase, cizopasení) vozit (vozím, voz, vozil, vožen ~ vozen, voze, vožení ~ vození) navozit (navozím, navoz, navozil, navozen ~ navožen, navoziv, navození ~ navožení) chodit (chodím, choď, chodil, chozen, chodě, chození) nachodit (nachodím, nachoď, nachodil, nachozen, nachodiv, nachození) platit (platím, plať, platil, placen, platě, placení) naplatit (naplatím, naplať, naplatil, naplacen, zaplativ, naplacení) čistit (čistím, čisť ~ čisti, čistil, čištěn ~ čistěn, čistě, čištění ~ čistění) vyčistit (vyčistím, vyčisť ~ vyčisti, vyčistil, vyčištěn ~ vyčistěn, vyčistiv, vyčištění ~ vyčistění) pustit (pustím, pusť, pustil, puštěn, pustiv, puštění) napustit (napustím, napusť, napustil, napuštěn, napustiv, napuštění) vábit (vábím, vab ~ vábi, vábil, váben, vábě, vábení) učit (učím, uč, učil, učen, uče, učení) křivdit (křivdím, křivdi ~ křivď, křivdil, křivděn, křivdě, křivdění) mírnit (mírním, mírni, mírnil, mírněn, mírně, mírnění) jezdit (jezdím, jezdi, jezdil, ježděn ~ jezděn, jezdě, ježdění ~ jezdění) zpozdit (zpozdím, zpozdi, zpozdil, zpožděn, zpozdiv, zpoždění) zaostřit (zaostřím, zaostři, zaostřil, zaostřen, zaostřiv, zaostření) zvětšit (zvětším, zvětši, zvětšil, zvětšen, zvětšiv, zvětšení) oprostit (oprostím, oprosti ~ oprosť, oprostil, oproštěn, oprostiv, oproštění) roztříštit (roztříštím, roztříšti, roztříštil, roztříštěn, roztříštiv, roztříštění) [NOTE: SSJC says impv roztříšť or roztříšti; IJP's commentary also says this should be the case] opatřit (opatřím, opatři ~ opatř, opatřil, opatřen, opatřiv, opatření) brzdit (brzdím, brzdi ~ brzď, brzdil, brzděn ~ bržděn, brzdě, brzdění ~ brždění) přesvědčit (přesvědčím, přesvědči ~ přesvědč, přesvědčil, přesvědčen, přesvědčiv, přesvědčení) léčit (léčím, léči ~ leč, léčil, léčen, léče, léčení) loudit (loudím, loudi ~ luď, loudil, louděn, loudě, loudění) toužit (toužím, touži ~ tuž, toužil, toužen, touže, toužení) soudit (soudím, suď, soudil, souzen, soudě, souzení) koupit (koupím, kup, koupil, koupen, koupiv, koupení) rdousit (rdousím, rdousi ~ rdus, rdousil, rdoušen, rdouse, rdoušení) bloudit (bloudím, bloudi ~ bluď, bloudil, no PPP, bloudě, bloudění) vrátit (vrátím, vrať, vrátil, vrácen, vrátiv, vrácení) přiblížit (přiblížím, přibliž, přiblížil, přiblížen, přiblíživ, přiblížení) lícit (lícím, líci ~ lic, lícil, no PPP, líce, lícení) čepýřit (čepýřím, čepyř ~ čepýři, čepýřil, čepýřen, čepýře, čepýření) chýlit (chýlím, chyl, chýlil, chýlen, chýle, chýlení) půlit (půlím, pul, půlil, půlen, půle, půlení) půjčit (půjčím, půjč [IRREG], půjčil, půjčen, půjčiv, půjčení) trůnit (trůním, trůni, trůnil, no PPP, trůně, trůnění) podnítit (podnítím, podniť ~ [rare] podněť, podnítil, podnícen, podnítiv, podnícení) vštípit (vštípím, vštěp ~ vštip, vštípil, vštípen, vštípiv, vštípení)

In -ít:

ctít (ctím, cti, ctil, ctěn, ctě, ctění) clít (clím, cli, clil, clen, cle, clení) dštít (dštím, dšti, dštil, dštěn, dště, dštění) pohřbít (pohřbím, pohřbi, pohřbil, pohřben, pohřbiv, pohřbení) křtít (křtím, křti, křtil, křtěn, křtě, křtění) obelstít (obelstím, obelsti, obelstil, obelstěn, obelstiv, obelstění) mdlít (mdlím, mdli, mdlil, no PPP?, mdle, mdlení) [NOTE: also mdlít IV.2] mstít (mstím, msti, mstil, mstěn, mstě, mstění) mžít (mžím, mži, mžil, mžen, mže, mžení)

Variation:

long forms if stem ends in two or more consonants, otherwise short forms. Short form imperative also shortens long stem vowels (á é í ý ů ou -> a e i y u u); but note půjčit, with imperative půjč. Alternations between í and ě seem rare; SSJC mentions nítit and podnítit with imperative (pod)niť or rarely (pod)něť; IJP has both and mentions that the latter is rare. SSJC also mentions svítit with imperative sviť or rarely svěť but IJP says only sviť. SSJC also mentions vštípit with imperative vštěp or vštip; IJP agrees. SSJC mentions vybílit with imperative vybil or rarely vyběl; IJP says imperative vybil or vybíli. SSJC has two vytížit verbs, the second of which has imperative only vytěž; IJP doesn't include this verb, only the first vytížit. generally occur as the last stem consonant). But not always, and sometimes both iotated and non-iotated variants occur. When non-iotated variants of t/d occur, they are palatalized, e.g. loudit -> 'louděn', ctit -> 'ctěn'.
 * Imperative can have long forms, short forms or both. Defaults to both forms if stem ends in -st, -št or -tř, otherwise
 * PPP's usually iotate coronals: s -> š, z -> ž, st -> šť, zd -> žď, t -> c, d -> z, n -> ň (velars and plain r do not

Variant codes: - Use 'long' for long imperative, 'short' for short imperative, 'long:short' or 'short:long' for both (default as above). - Use 'iot' for iotated PPP, 'ni' for non-iotated PPP, 'iot:ni' or 'ni:iot' for both (default is iotated).
 * čistit: IV.1.imp^short^long.ppp^iot^ni
 * brzdit: IV.1.imp^long^short.ppp^ni^iot
 * spasit: IV.1.ppp^ni
 * vozit: IV.1.ppp^iot^ni
 * loudit: IV.1.imp^long^short.ppp^ni

]=]

conj["IV.1"] = { get_infstem = function(base) -- Some with -ít e.g. pohřbít local infstem = separate_stem_suffix(base.lemma, "^(.*)[ií]t$", "IV.1") return com.convert_paired_plain_to_palatal(infstem) end, pres1s = "ím", imp = { choices = {"long", "short", "short-ě"}, default = function(base) return get_imptypes_for_stem(base, base.infstem) end, generate_part = function(base, variant) return get_imperative_principal_part_for_imptype(base, base.infstem, variant) end, },	past = "il", ppp = { choices = {"iot", "ni"}, default = "iot", generate_part = function(base, variant) if variant == "iot" then local iotated_stem = com.iotate(base.infstem) return com.combine_stem_ending(base, "ppp_m", iotated_stem, "en") elseif variant == "ni" then return com.combine_stem_ending(base, "ppp_m", base.infstem, "en") else error("Internal error: Saw unrecognized PPP variant code '" .. variant .. "'") end end, }, }

--[=[ IV.2:

bdít (bdím # bdí, bdi, bděl, bděn, bdě, bdění) čnět ~ čnít (čním # čnějí ~ ční, čni, čněl, čněn, čněje ~ čně, čnění) čpět ~ čpít (čpím # čpí, čpi, čpěl, čpěn, čpě, čpění) dít [biasp] (dím # dějí, děj, děl, no PPP, děje / děv, no VN) dlít (dlím # dlejí ~ dlí, dli, dlel, dlen, dle ~ dleje, dlení) hřmět ~ hřmít (hřmím # hřmí ~ hřmějí, hřmi, hřměl, no PPP, hřmě ~ presumably hřměje, hřmění) lpět ~ lpít (lpím # lpějí ~ lpí, lpi ~ lpěj, lpěl, lpěn, lpěje ~ lpě, lpění) mdlít (mdlím # mdlejí ~ mdlí, mdli, mdlel, no PPP?, mdleje ~ presumably mdle, mdlení) [NOTE: also mdlít IV.1] etc.

trpět [default] (trpím # trpí, trp, trpěl, trpěn, trpě, trpění) bolet /preslong:short (bolím # bolejí ~ bolí, bol, bolel, no PPP, boleje ~ bole, bolení) hovět /preslong:short,implong:short (hovím # hovějí ~ hoví, hověj ~ hov, hověl, no PPP, hověje ~ hově, hovění) náležet (náležím # náležejí ~ náleží, náležej ~ nálež, náležel, náležen, náleže ~ náležeje, náležení) souviset (souvisím # souvisejí ~ souvisí, souvisej, souvisel, no PPP, souvise ~ souviseje, souvisení) šumět (šumím # šumějí ~ šumí, šuměj ~ šum, šuměl, no PPP, šuměje ~ šumě, šumění) večeřet (večeřím # večeřejí ~ večeří, večeř, večeřel, no PPP, večeře, večeření) záviset (závisím # závisejí ~ závisí, závisej, závisel, no PPP, závise ~ záviseje, závisení) zmizet (zmizím # zmizejí ~ zmizí, zmiz, zmizel, zmizen, zmizev, zmizení) čumět (čumím # čumějí ~ čumí, čum, čuměl, no PPP, čuměje ~ čumě, čumění) slyšet (slyším # slyší, slyš ~ poslyš ["perceive by hearing"], slyšel, slyšen, slyše, slyšení) běžet (běžím # běží, běž ~ poběž, běžel, běžen, běže, běžení; fut. poběžím ... poběží) letet (letím # letí, leť ~ poleť, letěl, letěn, letě, letění; fut. poletím ... poletí) vidět (vidím # vidí, viz [IRREG], viděl, viděn, vida [IRREG], vidění)

]=]

--[=[ dělat "to do" V.1 konat "to act" V.1 chovat "to behave" V.1 doufat "to hope" V.1 ptát se "to ask" (vn 'ptaní se') V.1.vn:ptaní dbát "to care" (vn 'dbání ~ dbaní') V.1.vn:dbání:dbaní zanedbat "to neglect" V.1 znát "to know" V.1 poznat "to know (pf.)" V.1 poznávat "to know (secondary impf.)" V.1 nechat "to let (pf)" (imperative 'nech ~ nechej') V.1.imp:nech:nechej nechávat "to let (impf)" V.1 obědvat "to lunch" V.1 odolat "to resist"/zdolat/udolat V.1 plácat "to slap" V.1 drncat "to rattle" V.1 kecat "to chatter" V.1 cucat "to suck" V.1 ]=]

conjs["V.1"] = { get_infstem = function(base) return separate_stem_suffix(base.lemma, "^(.*)[aá]t$", "V.1") end, pres1s = "ám", imp = "ej", past = "al", ppp = "án", }

--[=[

tesat "to carve" (tesám ~ tešu, tesej ~ teš, tesal, tesán, tesaje ~ teše, tesání; tr.ppp): imp^a^e.prtr^a^e česat "to comb" (češu ~ česám, češ ~ česej, česal, česán, češe ~ česaje, česání; tr.ppp): pres^e^a.imp^e^a.prtr^e^a klusat "to trot" (klusám ~ klušu, klusej, klusal, no PPP, klusaje ~ kluše, klusání; intr): prtr^a^e křesat "to scrape" (křesám ~ křešu, křesej ~ křeš, křesal, no PPP, křesaje, křesání; tr.-ppp): imp^a^e řezat "to cut" (řežu ~ řezám, řež ~ řežej, řezal, řezán, řeže ~ řezaje, řezání; tr.ppp): pres^e^a.imp^e^a.prtr^e^a lízat "to lick" (lízám ~ lížu, lízej ~ liž [NOTE: short vowel], lízal, no PPP, lízaje ~ líže, lízání; tr.-ppp): imp^a^e.prtr^a^e hryzat ~ hrýzat "to bite" (hryzám ~ hryžu, hryzej ~ hryž, hryzal, hryzán, hryzaje ~ hryže, hryzání; tr.ppp): imp^a^e.prtr^a^e pásat se "to graze" (not in IJP) přepásat (přepásám ~ přepášu, přepásej, přepásal, přepásán, přepásav, přepásání; tr.ppp): [default] klouzat "to slide" (klouzám ~ kloužu, klouzej, klouzal, klouzán, klouzaje, klouzání; intr PPP?) [default]
 * Verbs in [sz]:

hýbat "to move" (hýbám ~ hýbu, hýbej, hýbal, no PPP, hýbaje, hýbání; intr no PPP) [default] dlabat "to gouge" (dlabám ~ dlabu, dlab ~ dlabej, dlabal, dlabán, dlabaje, dlabání): imp^e^a škrábat ~ škrabat "to scratch" (škrábám ~ škrábu, škrábej ~ škrab [NOTE: short vowel], škrábal, škrábán, škrábaje, škrábání): imp^e^a klepat "to knock" (klepám ~ klepu, klep ~ klepej, klepal, klepán, klepaje, klepání): imp^e^a kopat "to kick" (kopám ~ kopu, kopej, kopal, kopán, kopaje, kopání) [default] koupat "to bathe" (koupám ~ koupu, koupej, koupal, koupán, koupaje, koupání) [default] sypat "to sprinkle" (sypám ~ sypu, syp ~ sypej, sypal, sypán, sypaje, sypání): imp^e^a drápat "to claw" (drápám ~ drápu, drápej, drápal, drápán, drápaje, drápání) [default] dupat "to stomp" (dupám ~ dupu, dupej, dupal, dupán, dupaje, dupání) [default] loupat "to peel" (loupám ~ loupu, loupej, loupal, loupán, loupaje, loupání) [default] rýpat "to dig" (rýpám ~ rýpu, rýpej, rýpal, rýpán, rýpaje, rýpání) [default] štípat "to pinch" (štípám ~ štípu, štípej, štípal, štípán, štípaje, štípání) [default] šlapat "to step; to trample" (šlapám ~ šlapu, šlap ~ šlapej, šlapal, šlapán, šlapaje, šlapání): imp^e^a tápat "to grope" (tápám ~ tápu, tápej, tápal, no PPP, tápaje, tápání; intr) [default] dřímat "to doze" (dřímám ~ dřímu, dřímej, dřímal, no PPP, dřímaje, dřímání; intr) [default] klamat "to deceive" (klamu, klamej ~ klam, klamal, klamán, klamaje, klamání): pres^e.imp^a^e lámat "to break" (lámu ~ lámam [rare per SSJC; not in IJP except as a note], lam [NOTE: short vowel] ~ lámej, lámal, lámán, lámaje, lámání): pres^e^a[rare].imp^e^a plavat "to swim, to float" (plavu, plavej ~ plav ~ poplav, plaval, plaván, plavaje, plavání) [NOTE: fut either 'budu plavat, budeš plavat, etc.' or 'poplavu, poplaveš, etc.']: pres^e.imp^a^e:poplav.fut:+:poplavu klofat "to peck; to tap, to knock" (klofám ~ klofu, klofej, klofal, klofán, klofaje, klofání) [default] hrabat (hrabu, hrab ~ hrabej, hrabal, hrabán, hrabaje, hrabání): pres^e.imp^e^a zahrabat (zahrabu, zahrabej ~ zahrab, zahrabal, zahrabán, zahrabav, zahrabání): pres^e.imp^a^e
 * Verbs in [bpvfm]:

orat "to plow" (orám ~ ořu, orej ~ oř, oral, orán, oraje, orání): imp^a^e párat "to unstitch; to unravel" (párám ~ pářu, párej, páral, párán, páraje, párání) [default] dudlat "to hum, to drone (of an instrument or musician); to grumble, to groan; to suck (one's thumb, etc.; of a child)" (not in IJP; SSJC says pres dudlám ~ dudlu, impv dudlej ~ dudli; tr no PPP): imp^a^e stonat "to moan, to groan" (stůňu/stůněš/etc., stonej, stonal, no PPP, stonaje, stonání): pres:stůňu
 * Verbs in [rln]:

týkat se (týči se ~ týču se ~ týkám se, týkej se, týkal se, no PPP, týče se ~ týkaje se, týkání (se)) kdákat (kdáču ~ kdákám, kdákej, kdákal, kdákán, kdáče ~ kdákaje, kdákání) kvákat (kváču ~ kvákám, kvákej, kvákal, no PPP, kváče ~ kvákaje, kvákání) páchat (páchám ~ pášu, páchej, páchal, páchán, páchaje, páchání)
 * Verbs in [kh] and -ch:

[none; all verbs given in Wikipedia as examples are either V.1 or missing in IJP]
 * Verbs in [td]:

Types: ]=]
 * [default] = a-stem + e-stem, impv only a-stem, prtr only a-stem = klusat, přepásat, klouzat, hýbat, škrabat, kopat, koupat, drápat, dupat, loupat, rýpat, štípat, tápat, dřímat, klamat, klofat, párat, páchat
 * pres^e^a.prtr^e^a = e-stem + a-stem, impv only a-stem = týkat se, kdákat, kvákat
 * pres:stůňu = e-stem, impv only a-stem = stonat
 * imp^e^a = a-stem + e-stem, impv e-stem + a-stem = dlabat, klepat, sypat, šlapat, lámat (short 'lam')
 * pres^e^a.imp^e^a.prtr^e^a = e-stem + a-stem, impv e-stem + a-stem = česat, řezat
 * imp^a^e = a-stem + e-stem, impv a-stem + e-stem = tesat, křesat, lízat, hryzat, orat, dudlat
 * pres^e.imp^a^e = e-stem, impv only a-stem + e-stem = plavat

conjs["V.2"] = { init = function(base) base.infstem = separate_stem_suffix(base.lemma, "^(.*)[aá]t$", "V.2") base.palstem = com.apply_first_palatalization(base.infstem, "is verb") end, pres1s = { choices = {"a", "e"}, default = {"a", "e"}, generate_part = function(base, variant) if variant == "a" then return base.infstem .. "ám" elseif variant == "e" then return base.palstem .. "u" else error("Internal error: Saw unrecognized pres1s variant code '" .. variant .. "'") end end, },	imp = { choices = {"a", "e"}, default = {"a"}, generate_part = function(base, variant) if variant == "a" then return base.infstem .. "ej" else return generate_default_imperative_principal_part_from_stem(base, base.palstem) end end, },	past = "al", ppp = "án", prtr = { choices = {"a", "e"}, default = {"a"}, generate_part = function(base, variant) if variant == "a" then return base.infstem .. "aje" else return com.combine_stem_ending(base, "pres_tgress_m", base.palstem, "ě") end end, }, }

conjs["5"] = function(base, lemma) local stem, suffix, ac = separate_stem_suffix_accent(lemma, "5", "^(.*)([іая])(́?)ти$") local stem_ends_in_vowel = com.ends_in_vowel(stem) if suffix == "я" and not stem_ends_in_vowel then error("Ending -яти can only be used with a vocalic stem: '" .. lemma .. "'") elseif suffix ~= "я" and stem_ends_in_vowel then error("Ending -яти must be used with a vocalic stem: '" .. lemma .. "'") end local stressed_stem = com.maybe_stress_final_syllable(stem) local sg2 if base.i then if not rfind(stem, "о́?$") then error("і-modifier can only be used with stem ending in -о: '" .. lemma .. "'") end sg2 = com.maybe_stress_final_syllable(rsub(stem, "о(́?)$", "і%1й")) end add_present_i(base, stressed_stem, sg2) add_past(base, stem .. suffix .. ac) add_retractable_ppp(base, (suffix == "і" and com.iotate(stem) .. "е" or stem .. suffix) .. ac .. "н") end

conjs["8"] = function(base, lemma) local stem, last_cons = rmatch(lemma, "^(.*)([кг])ти́?$") if not stem then error("Unrecognized lemma for class 8: '" .. lemma .. "'") end local palatalized_cons = com.iotate(last_cons) local stressed_stem = com.maybe_stress_final_syllable(stem) add_present_e(base, stressed_stem .. palatalized_cons) local past_msg = stressed_stem .. last_cons local past_rest = past_msg .. "л" if base.i then past_msg = rsub(past_msg, "[еоя](́?" .. com.cons_c .. "+)$", "і%1") end add_past(base, past_msg, past_rest) add_ppp(base, stressed_stem .. palatalized_cons .. "ен") end

conjs["9"] = function(base, lemma) local stem, suffix = rmatch(lemma, "^(.*)(е́?р)ти$") if not stem then error("Unrecognized lemma for class 9: '" .. lemma .. "'") end if accent ~= "a" and accent ~= "b" then error("Only accent a or b allowed for class 9: '" .. base.conj .. "'") end local pres_stem if base.conj_star then pres_stem = rsub(stem, "^(.*)(.)$", "%1і%2") else pres_stem = stem end add_present_e(base, pres_stem .. "р") local stressed_stem = com.maybe_stress_final_syllable(stem .. suffix) add_past(base, stressed_stem, stressed_stem .. "л") add_ppp(base, stressed_stem .. "т") end

conjs["10"] = function(base, lemma) local stem, suffix, ac = separate_stem_suffix_accent(lemma, "10", "^(.*)(о[лр]о)(́?)ти$") if accent ~= "a" and accent ~= "c" then error("Only accent a or c allowed for class 10: '" .. base.conj .. "'") end local stressed_stem = com.maybe_stress_final_syllable(rsub(stem .. suffix, "о$", "")) add_present_e(base, stressed_stem, "1sg3pl") add_past(base, stem .. suffix .. ac) -- If explicit present stem given (e.g. for моло́ти), use it/them in the н-participle. local n_ppps if base.pres_stems then n_ppps = {} for _, pres_stem in ipairs(base.pres_stems) do table.insert(n_ppps, pres_stem .. "ен") end else n_ppps = stressed_stem .. "ен" end local t_ppp = stressed_stem .. "от" if base.conj == "н" then add_ppp(base, n_ppps) elseif base.conj == "т" then add_ppp(base, t_ppp) else add_ppp(base, n_ppps) add_ppp(base, t_ppp) end end

conjs["11"] = function(base, lemma) local stem, suffix, ac = separate_stem_suffix_accent(lemma, "11", "^(.*)(и)(́?)ти$") if accent ~= "a" and accent ~= "b" then error("Only accent a or b allowed for class 11: '" .. base.conj .. "'") end local pres_stem if base.conj_star then pres_stem = rsub(stem, "^(.*)(.)$", "%1і%2") else pres_stem = stem end if rfind(pres_stem, "л$") then pres_stem = pres_stem .. "л" else pres_stem = pres_stem .. "'"	end local full_stem = stem .. suffix .. ac add_present_e(base, pres_stem, "all", full_stem .. "й") add_past(base, full_stem) add_ppp(base, full_stem .. "т") end

conjs["12"] = function(base, lemma) local stem = rmatch(lemma, "^(.*" .. com.vowel_c .. AC .. "?)ти$") if not stem then error("Unrecognized lemma for class 12: '" .. lemma .. "'") end if accent ~= "a" and accent ~= "b" then error("Only accent a or b allowed for class 12: '" .. base.conj .. "'") end add_present_e(base, stem) add_past(base, stem) add_ppp(base, stem .. "т") end

conjs["13"] = function(base, lemma) local stem = rmatch(lemma, "^(.*а)ва́ти$") if not stem then error("Unrecognized lemma for class 13: '" .. lemma .. "'") end if accent ~= "b" then error("Only accent b allowed for class 13: '" .. base.conj .. "'") end local full_stem = stem .. "ва́" add_present_e(base, stem, nil, full_stem .. "й") add_past(base, full_stem) add_retractable_ppp(base, full_stem .. "н") end

conjs["14"] = function(base, lemma) -- -сти occurs in п'я́сти́ and роз(і)п'я́сти́ local stem = rmatch(lemma, "^(.*[ая]́?)с?ти́?$") if not stem then error("Unrecognized lemma for class 14: '" .. lemma .. "'") end if not base.pres_stems then error("With class 14, must specify explicit present stem using 'pres:STEM'") end add_present_e(base, "foo") local stressed_stem = com.maybe_stress_final_syllable(stem) add_past(base, stressed_stem) add_retractable_ppp(base, stressed_stem .. "т") end

conjs["irreg"] = function(base, lemma) local prefix = rmatch(lemma, "^(.*)да́?ти$") if prefix then local stressed_prefix = com.is_stressed(prefix) if stressed_prefix then add_pres_fut(base, prefix, "дам", "даси", "дасть", "дамо", "дасте", "дадуть") add_imperative(base, prefix .. "дай") add_past(base, prefix .. "да") add_retractable_ppp(base, prefix .. "дан") -- ви́даний from ви́дати else add_pres_fut(base, prefix, "да́м", "даси́", "да́сть", "дамо́", "дасте́", "даду́ть") add_imperative(base, prefix .. "да́й") add_past(base, prefix .. "да́") add_retractable_ppp(base, prefix .. "да́н") -- e.g. пере́даний from переда́ти end return end prefix = rmatch(lemma, "^(.*по)ві́?сти́?$") if prefix then local stressed_prefix = com.is_stressed(prefix) if stressed_prefix then add_pres_fut(base, prefix, "вім", "віси", "вість", "вімо", "вісте", "відять") add_imperative(base, prefix .. "відж", "[lc]") add_imperative(base, prefix .. "віж", "[lc]") add_past(base, prefix .. "ві") -- no PPP else add_pres_fut(base, prefix, "ві́м", "віси́", "ві́сть", "вімо́", "вісте́", "відя́ть") add_imperative(base, prefix .. "ві́дж", "[lc]") add_imperative(base, prefix .. "ві́ж", "[lc]") add_past(base, prefix .. "ві́") -- no PPP end return end prefix = rmatch(lemma, "^(.*)ї́?сти$") if prefix then local stressed_prefix = com.is_stressed(prefix) if stressed_prefix then add_pres_fut(base, prefix, "їм", "їси", "їсть", "їмо", "їсте", "їдять") add_imperative(base, prefix .. "їж") add_past(base, prefix .. "ї") add_ppp(base, prefix .. "їден") -- ви́їдений from ви́їсти else add_pres_fut(base, prefix, "ї́м", "їси́", "ї́сть", "їмо́", "їсте́", "їдя́ть") add_imperative(base, prefix .. "ї́ж") add_past(base, prefix .. "ї́") add_ppp(base, prefix .. "ї́ден") -- e.g. прої́дений from прої́сти end return end prefix = rmatch(lemma, "^(.*)бу́?ти$") if prefix then local stressed_prefix = com.is_stressed(prefix) if prefix == "" then error("Can't handle unprefixed irregular verb бу́ти yet") end add_present_e(base, prefix .. (stressed_prefix and "буд" or "бу́д"), "a") add_past(base, prefix .. (stressed_prefix and "бу" or "бу́")) add_ppp(base, prefix .. (stressed_prefix and "бут" or "бу́т")) -- e.g. забу́тий from забу́ти return end prefix = rmatch(lemma, "^(.*)ї́?хати$") if prefix then local stressed_prefix = com.is_stressed(prefix) add_present_e(base, prefix .. (stressed_prefix and "їд" or "ї́д"), "a") add_past(base, prefix .. (stressed_prefix and "їха" or "ї́ха")) -- no PPP return end prefix = rmatch(lemma, "^(.*)шиби́?ти$") if prefix then local stressed_prefix = com.is_stressed(prefix) add_present_e(base, prefix .. "шиб", stressed_prefix and "a" or "b") local past_msg = prefix .. (stressed_prefix and "шиб" or "ши́б") add_past(base, past_msg, past_msg .. "л") add_ppp(base, prefix .. (stressed_prefix and "шиблен" or "ши́блен")) -- e.g. проши́блений from прошиби́ти return end prefix = rmatch(lemma, "^(.*соп)і́ти$") if prefix then add_pres_fut(base, prefix, "лю́", "е́ш", "е́", {"е́м", "емо́"}, "ете́", "ля́ть") add_imperative(base, prefix .. "и́") add_past(base, prefix .. "і́") -- no PPP return end prefix = rmatch(lemma, "^(.*)жи́?ти$") if prefix then local stressed_prefix = com.is_stressed(prefix) add_present_e(base, prefix .. "жив", stressed_prefix and "a" or "b") add_past(base, prefix .. (stressed_prefix and "жи" or "жи́")) add_ppp(base, prefix .. (stressed_prefix and "жит" or "жи́т")) -- e.g. пережи́тий from пережи́ти return end prefix = rmatch(lemma, "^(.*)бі́?гти$") if prefix then local stressed_prefix = com.is_stressed(prefix) add_present_i(base, prefix .. "біж", stressed_prefix and "a" or "b") local past_msg = prefix .. (stressed_prefix and "біг" or "бі́г") add_past(base, past_msg, past_msg .. "л") -- no PPP return end prefix = rmatch(lemma, "^(п?і)ти́$") if not prefix then prefix = rmatch(lemma, "^(.*й)ти́?$") end if prefix then local stressed_prefix = com.is_stressed(prefix) add_present_e(base, com.maybe_stress_final_syllable(prefix .. "д"),			stressed_prefix and "a" or (prefix == "і" or prefix == "й") and "b" or "c") add_past(base, prefix .. (stressed_prefix and "шов" or "шо́в"), com.maybe_stress_final_syllable(prefix .. "шл")) add_retractable_ppp(base, prefix .. (stressed_prefix and "ден" or "де́н")) -- e.g. пере́йдений from перейти́ return end error("Unrecognized irregular verb: '" .. lemma .. "'") end

local function add_infinitive(base) add(base, "infinitive", base.lemma, "") -- FIXME: Consider adding old infinitive in -ti as an alternant at the end (after adding imperfective future) end

local function set_present_future(base) local forms = base.forms if base.aspect == "pf" then for slot_suffix, _ in pairs(budu_forms) do forms["fut_" .. slot_suffix] = forms["pres_fut_" .. slot_suffix] forms["pres_fut_" .. slot_suffix] = nil end else for slot_suffix, _ in pairs(budu_forms) do forms["pres_" .. slot_suffix] = forms["pres_fut_" .. slot_suffix] forms["pres_fut_" .. slot_suffix] = nil end -- Do the periphrastic future with budu if forms.infinitive then for slot_suffix, budu_form in pairs(budu_forms) do local futslot = "fut_" .. slot_suffix if not skip_slot(base, futslot) then iut.insert_forms(forms, futslot, iut.map_forms(forms.infinitive, function(form) if not form:find("%[") then form = "" .. form .. "" end return "" .. budu_form .. "" .. TEMP_REFLEXIVE_INSERTION_POINT .. " " .. form end)) end end end end end

-- Generate a composed tense. `pref` is the prefix of the tense (e.g. "past") and `templates` is a 6-element list of -- templates used to generate the composed tense. Each template should have a %s where the l-participle is substituted -- and a * where TEMP_REFLEXIVE_INSERTION_POINT is substituted, indicating where the reflexive clitic should go. -- `dual_template` is true if each template has two slots in it (the first for the l-participle of být, the second -- for the l-participle of the verb itself). local function add_composed_tense(base, pref, templates, dual_template) for _, props in ipairs(person_number_gender_props) do		local dest_suffix, part_suffix, template_index = unpack(props) local template = templates[template_index] template = template:gsub("%*", TEMP_REFLEXIVE_INSERTION_POINT) iut.insert_forms(base.forms, pref .. "_" .. dest_suffix, iut.map_forms(base.forms["lpart_" .. part_suffix], function(form) if not form:find("%[") then form = "" .. form .. "" end if dual_template then local byl_form = "byl" .. part_suffix_to_ending[part_suffix] .. "" return template:format(byl_form, form) else return template:format(form) end end)) end end

local function generate_composed_tenses(base) -- Then generate the past tense by combining the l-participle with the present tense of být. add_composed_tense(base, "past", {"%s jsem*", "%s jsi*", "%s*", "%s jsme*", "%s jste*", "%s*"}) add_composed_tense(base, "cond", {"%s bych*", "%s bys*", "%s by*", "%s bychom*", "%s byste*", "%s by*"}) add_composed_tense(base, "cond_past", {"%s bych* %s", "%s bys* %s", "%s by* %s",		"%s bychom* %s", "%s byste* %s", "%s by* %s"}, "dual template") end

-- Add a reflexive pronoun as appropriate to the base forms that were generated. local function add_reflexive_to_forms(base) if not base.refl then -- Remove insertion point character. for slot, accel in pairs(verb_slots) do			if base.forms[slot] then for _, form in ipairs(base.forms[slot]) do					form.form = form.form:gsub(TEMP_REFLEXIVE_INSERTION_POINT, "") end end end return end

clitic = " " .. base.refl .. "" local paren_clitic = " (" .. base.refl .. ")" for slot, accel in pairs(verb_slots) do		if base.forms[slot] then local this_clitic = slot == "vnoun" and paren_clitic or clitic -- Add clitic as separate word before all other forms. for _, form in ipairs(base.forms[slot]) do				if form.form:find(TEMP_REFLEXIVE_INSERTION_POINT) then form.form = form.form:gsub(TEMP_REFLEXIVE_INSERTION_POINT, this_clitic) else if not form.form:find("%[") then form.form = "" .. form.form .. "" end form.form = form.form .. this_clitic end form.form = form.form:gsub("%[%[bys%]%] %[%[(s[ei])%]%]", "by %1s") form.form = form.form:gsub("%[%[jsi%]%] %[%[(s[ei])%]%]", "%1s") end end end end

local function conjugate_verb(base) add_infinitive(base) conjs[base.conj](base, base.lemma) set_present_future(base) generate_composed_tenses(base) add_reflexive_to_forms(base) end

local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do		if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'") end if not footnotes then footnotes = {} end table.insert(footnotes, separated_group[j]) end return footnotes end

local function parse_indicator_spec(angle_bracket_spec) local inside = rmatch(angle_bracket_spec, "^<(.*)>$") assert(inside) local function parse_err(msg) error(msg .. ": '" .. inside .. "'") end local base = {overrides = {}, forms = {}} local segments = iut.parse_balanced_segment_run(inside, "[", "]") local dot_separated_groups = iut.split_alternating_runs_and_strip_spaces(segments, "%.") local major_class = dot_separated_groups[1][1] if major_class ~= "I" and major_class ~= "II" and major_class ~= "III" and major_class ~= "IV" and major_class ~= "V" and major_class ~= "irreg" then parse_err("Unrecognized major verb class '" .. major_class .. "'; expected 'I', 'II', 'III', 'IV', 'V' or 'irreg'") end if #dot_separated_groups[1] > 1 then parse_err("No footnotes allowed after major class") end local start_of_indicators = major_class == "irreg" and 2 or 3 if major_class == "irreg" then base.conj = "irreg" else local minor_class_and_variants = dot_separated_groups[2][1] local minor_class, variants = rmatch(minor_class_and_variants, "^([123])/(.*)$") if not minor_class then minor_class = rmatch(minor_class_and_variants, "^([123])$") end if not minor_class then parse_err("Unrecognized minor verb class; expected 1, 2 or 3") end base.conj = major_class .. "." .. minor_class if variants then dot_separated_groups[2][1] = variants if parse[base.conj] then local function parse_err(msg) error(msg .. ": '" .. table.concat(dot_separated_groups[2]) .. "'") end parse[base.conj](base, dot_separated_groups[2], parse_err) else parse_err("No variants allowed for conjugation " .. base.conj) end elseif #dot_separated_groups[2] > 1 then parse_err("No footnotes allowed after minor class") end end for i, dot_separated_group in ipairs(dot_separated_groups) do		if i >= start_of_indicators then local part = dot_separated_group[1] local stem, rest = rmatch(part, "^([a-z_]+):(.*)$") if override_stems[stem] then if base.overrides[stem] then parse_err(("Two overrides specified for stem '%s'"):format(stem)) end base.overrides[stem] = {} dot_separated_group[1] = rest local colon_separated_groups = iut.split_alternating_runs_and_strip_spaces(dot_separated_group, ":") for i, colon_separated_group in ipairs(colon_separated_groups) do					local form = colon_separated_group[1] if form == "" then -- No need to use parse_err as the overall spec is probably irrelevant error(("Use - to indicate a missing stem '%s': '%s'"):format(stem, table.concat(dot_separated_group))) elseif form == "-" then if #colon_separated_group > 1 then error(("No footnotes allowed with '-' as stem value for stem '%s': '%s'"):format(stem, table.concat(dot_separated_group))) end -- don't record a value else local value = {} value.form = form value.footnotes = fetch_footnotes(colon_separated_group) table.insert(base.overrides[stem], value) end end elseif part == "" then if #dot_separated_group == 1 then error("Blank indicator: '" .. inside .. "'") end base.footnotes = fetch_footnotes(dot_separated_group) elseif #dot_separated_group > 1 then error("Footnotes only allowed with stem overridese or by themselves: '" .. table.concat(dot_separated_group) .. "'") elseif part == "impf" or part == "pf" or part == "both" then if base.aspect then parse_err("Can't specify aspect twice") end base.aspect = part elseif part == "tr" or part == "intr" or part == "mixed" then if base.trans then parse_err("Can't specify transitivity twice") end base.trans = part elseif part == "ppp" or part == "-ppp" then if base.ppp ~= nil then parse_err("Can't specify past passive participle indicator twice") end base.ppp = part == "ppp" elseif part == "impers" or part == "3only" or part == "plonly" or part == "3plonly" or part == "3orplonly" or				part == "ě" then local field = part if part == "ě" then field = "ye" end if base[field] then parse_err(("Can't specify '%s' twice"):format(part)) end base[field] = true else error("Unrecognized indicator '" .. part .. "': " .. angle_bracket_spec) end end end return base end

local function normalize_all_lemmas(alternant_multiword_spec, pagename) iut.map_word_specs(alternant_multiword_spec, function(base)		if base.lemma == "" then			base.lemma = pagename		end		base.orig_lemma = base.lemma		base.orig_lemma_no_links = m_links.remove_links(base.lemma)		-- If reflexive verb is explicitly specified by the user, we will convert the space before the reflexive clitic		-- to an underscore in split_bracketed_runs_into_words.		local active_verb, refl = rmatch(base.orig_lemma_no_links, "^(.*)[ _](s[ei])$")		if active_verb then			base.refl = refl			base.lemma = active_verb		else			base.lemma = base.orig_lemma_no_links		end		-- Convert "old-style" lemma e.g. dělati, nésti, moci into new-style dělat, nést, moct		local old_style_stem = rmatch(base.lemma, "^(.*)i$")		if old_style_stem then			if rfind(old_style_stem, "c$") then				-- moci, peci, etc.				base.lemma = old_style_stem .. "t"			elseif rfind(old_style_stem, "t$") then base.lemma = old_style_stem else error(("Unrecognized old-style lemma '%s', should end in -ci or -ti"):format(base.orig_lemma_no_links)) end end end) end

local function detect_indicator_spec(base) 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.orig_lemma_no_links .. "'") end elseif not base.trans then error("Transitivity of 'tr', 'intr' or 'mixed' must be specified") end if base.ppp ~= nil then if base.trans == "intr" then error("Can't specify 'ppp' or '-ppp' with intransitive verbs") end elseif base.trans and base.trans ~= "intr" then error("Must specify 'ppp' or '-ppp' with transitive or mixed-transitive verbs") end end

local function detect_all_indicator_specs(alternant_multiword_spec) iut.map_word_specs(alternant_multiword_spec, function(base)		detect_indicator_spec(base)		if not alternant_multiword_spec.aspect then			alternant_multiword_spec.aspect = base.aspect		elseif alternant_multiword_spec.aspect ~= base.aspect then			alternant_multiword_spec.aspect = "both"		end		if alternant_multiword_spec.refl == nil then			alternant_multiword_spec.refl = base.refl		elseif alternant_multiword_spec.refl ~= base.refl then			error("With multiple alternants, all must agree on reflexive clitic")		end		if not alternant_multiword_spec.trans then			alternant_multiword_spec.trans = base.trans		elseif alternant_multiword_spec.trans ~= base.trans then			alternant_multiword_spec.trans = "mixed"		end		for _, prop in ipairs({"nopres", "noimp", "nopast", "impers", "only3", "onlypl", "only3pl", "only3orpl"}) do			if alternant_multiword_spec[prop] == nil then				alternant_multiword_spec[prop] = base[prop] elseif alternant_multiword_spec[prop] ~= base[prop] then alternant_multiword_spec[prop] = false end end end) end

local function add_categories(alternant_multiword_spec) local cats = {} local function insert(cattype) table.insert(cats, "Czech " .. cattype .. " verbs") end if alternant_multiword_spec.aspect == "impf" then insert("imperfective") elseif alternant_multiword_spec.aspect == "pf" then insert("perfective") else assert(alternant_multiword_spec.aspect == "both") insert("imperfective") insert("perfective") insert("biaspectual") end if alternant_multiword_spec.trans == "tr" then insert("transitive") elseif alternant_multiword_spec.trans == "intr" then insert("intransitive") elseif alternant_multiword_spec.trans == "mixed" then insert("transitive") insert("intransitive") end if alternant_multiword_spec.is_refl then insert("reflexive") end if alternant_multiword_spec.impers then insert("impersonal") end iut.map_word_specs(alternant_multiword_spec, function(base)		if base.conj == "irreg" or base.irreg then			insert("irregular")		end		if base.conj ~= "irreg" then			insert("class " .. base.conj)			insert("class " .. rsub(base.conj, "^([0-9]+).*", "%1"))		end	end) alternant_multiword_spec.categories = cats end

local function show_forms(alternant_multiword_spec) local lemmas = {} if alternant_multiword_spec.forms.infinitive then for _, inf in ipairs(alternant_multiword_spec.forms.infinitive) do			table.insert(lemmas, inf.form) end end local props = { lemmas = lemmas, slot_table = verb_slots, lang = lang, }	iut.show_forms(alternant_multiword_spec.forms, props) end

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

local table_spec_overall = [=[ {title}{annotation} {\op}| class="inflection-table inflection inflection-cs inflection-verb" style="border: 2px solid black;" border=1 ! rowspan=3 colspan=2 style="background:#cddfff" | ! colspan=3 style="background:#cddfff; text-align: center;" | singular ! colspan=4 style="background:#cddfff; text-align: center;" | plural ! rowspan=2 style="background:#e7f3ff; text-align: center;vertical-align:middle;"| masculine ! rowspan=2 style="background:#e7f3ff; text-align: center;vertical-align:middle;"| feminine ! rowspan=2 style="background:#e7f3ff; text-align: center;vertical-align:middle;"| neuter ! colspan=2 style="background:#e7f3ff; text-align: center;"| masculine ! rowspan=2 style="background:#e7f3ff; text-align: center;vertical-align:middle;"| feminine ! rowspan=2 style="background:#e7f3ff; text-align: center;vertical-align:middle;"| neuter ! style="background:#e7f3ff; text-align: center;"| animate ! style="background:#e7f3ff; text-align: center;"| inanimate ! style="background:#e7f3ff; text-align: center; width:8%;"| invariable ! style="background:#cddfff; text-align: center; width:8%;"| infinitive ! rowspan=4 style="background:#e7f3ff; text-align: center; vertical-align: middle;"| number/gender only ! style="background:#cddfff; text-align: center;"| short passive participle ! style="background:#cddfff; text-align: center;"| l-participle ! style="background:#cddfff; text-align: center;"| present transgressive ! style="background:#cddfff; text-align: center;"| past transgressive ! rowspan=3 style="background:#e7f3ff; text-align: center; vertical-align: middle;"| declined as adjective ! style="background:#cddfff; text-align: center;"| present active participle ! style="background:#cddfff; text-align: center;"| past active participle ! style="background:#cddfff; text-align: center;"| long passive participle ! style="background:#e7f3ff; text-align: center;"| case/number only ! style="background:#cddfff; text-align: center; vertical-align: middle;"| verbal noun {indicative_header}{present_table}| {fut_1s} {past_table}{conditional_header}{cond_table}{cond_past_table}! style="background:#cddfff; text-align: center; border-top-width: 3px;" colspan=2 | imperative
 * colspan=7 | {infinitive}
 * {ppp_m}
 * {ppp_f}
 * {ppp_n}
 * {ppp_mp_an}
 * {ppp_mp_in}
 * {ppp_fp}
 * {ppp_np}
 * {lpart_m}
 * {lpart_f}
 * {lpart_n}
 * {lpart_mp_an}
 * {lpart_mp_in}
 * {lpart_fp}
 * {lpart_np}
 * {pres_tgress_m}
 * colspan=2|{pres_tgress_fn}
 * colspan=4|{pres_tgress_p}
 * {past_tgress_m}
 * colspan=2|{past_tgress_fn}
 * colspan=4|{past_tgress_p}
 * colspan=7 | {pres_act_part}
 * colspan=7 | {past_act_part}
 * colspan=7 | {long_pass_part}
 * style="vertical-align: middle;" colspan=7 | {vnoun}
 * {fut_2s}
 * {fut_3s}
 * {fut_1p}
 * colspan=2 | {fut_2p}
 * {fut_3p}
 * style="border-top-width: 3px;" | —
 * style="border-top-width: 3px;" | {imp_2s}
 * style="border-top-width: 3px;" | —
 * style="border-top-width: 3px;" | {imp_1p}
 * style="border-top-width: 3px;" colspan=2 | {imp_2p}
 * style="border-top-width: 3px;" | —
 * {\cl}{notes_clause} ]=]

local table_spec_person_number_header = [=[ !style="background:#cddfff; text-align: center; vertical-align: middle; border-top-width: 3px;" rowspan=3 colspan=2 | MOOD !style="background:#cddfff; text-align: center; border-top-width: 3px;" colspan=3 | singular !style="background:#cddfff; text-align: center; border-top-width: 3px;" colspan=4 | plural (or polite) !style="background:#e7f3ff; text-align: center; vertical-align: middle;" rowspan=2 | first !style="background:#e7f3ff; text-align: center; vertical-align: middle;" rowspan=2 | second !style="background:#e7f3ff; text-align: center; vertical-align: middle;" rowspan=2 | third !style="background:#e7f3ff; text-align: center; vertical-align: middle;" rowspan=2 | first !style="background:#e7f3ff; text-align: center; vertical-align: middle;" colspan=2 | second !style="background:#e7f3ff; text-align: center; vertical-align: middle;" rowspan=2 | third !style="background:#e7f3ff; text-align: center;" | polite singular !style="background:#e7f3ff; text-align: center;" | plural ]=]

local table_spec_single_aspect_present = [=[ ! style="background:#cddfff; text-align: center;" colspan=2 | present ! style="background:#cddfff; text-align: center;" colspan=2 | future ]=]
 * {pres_1s}
 * {pres_2s}
 * {pres_3s}
 * {pres_1p}
 * colspan=2 | {pres_2p}
 * {pres_3p}

local table_spec_biaspectual_present = [=[ ! style="background:#cddfff; text-align: center;" colspan=2 | present (imperfective) ! style="background:#cddfff; text-align: center;" colspan=2 | future (perfective) ! style="background:#cddfff; text-align: center;" colspan=2 | future (imperfective) ]=]
 * rowspan=2 | {pres_1s}
 * rowspan=2 | {pres_2s}
 * rowspan=2 | {pres_3s}
 * rowspan=2 | {pres_1p}
 * colspan=2 rowspan=2 | {pres_2p}
 * rowspan=2 | {pres_3p}

local table_spec_person_number_gender = [=[ !style="background:#cddfff; text-align: center; vertical-align: middle;" rowspan=4 | TENSE !style="background:#e7f3ff; text-align: center;"| masculine animate !style="background:#e7f3ff; text-align: center;"| masculine inanimate !style="background:#e7f3ff; text-align: center;"| feminine !style="background:#e7f3ff; text-align: center;"| neuter ]=]
 * rowspan=2 | {PREF_1sm}
 * rowspan=2 | {PREF_2sm}
 * rowspan=2 | {PREF_3sm}
 * rowspan=2 | {PREF_1pm}
 * rowspan=2 | {PREF_2pm_polite}
 * rowspan=2 | {PREF_2pm_plural}
 * {PREF_3pm_an}
 * {PREF_3pm_in}
 * {PREF_1sf}
 * {PREF_2sf}
 * {PREF_3sf}
 * {PREF_1pf}
 * {PREF_2pf_polite}
 * {PREF_2pf_plural}
 * {PREF_3pf}
 * {PREF_1sn}
 * {PREF_2sn}
 * {PREF_3sn}
 * {PREF_1pn}
 * {PREF_2pn_polite}
 * {PREF_2pn_plural}
 * {PREF_3pn}

local notes_template = [===[ {footnote} ]===]

if alternant_multiword_spec.title then forms.title = alternant_multiword_spec.title else forms.title = 'Conjugation of ' .. forms.lemma .. '' end

local ann_parts = {} local saw_irreg_conj = false local saw_base_irreg = false local all_irreg_conj = true local conjs = {} iut.map_word_specs(alternant_multiword_spec, function(base)		m_table.insertIfNot(conjs, base.conj)		if base.conj == "irreg" then			saw_irreg_conj = true		else			all_irreg_conj = false		end		if base.irreg then			saw_base_irreg = true		end	end) if all_irreg_conj then table.insert(ann_parts, "irregular") else table.insert(ann_parts, "class " .. table.concat(conjs, " // ")) end table.insert(ann_parts,		alternant_multiword_spec.aspect == "impf" and "imperfective" or		alternant_multiword_spec.aspect == "pf" and "perfective" or		"biaspectual") if alternant_multiword_spec.trans then table.insert(ann_parts,			alternant_multiword_spec.trans == "tr" and "transitive" or			alternant_multiword_spec.trans == "intr" and "intransitive" or			"transitive and intransitive"		) end if alternant_multiword_spec.is_refl then table.insert(ann_parts, "reflexive") end if alternant_multiword_spec.impers then table.insert(ann_parts, "impersonal") end if saw_base_irreg and not saw_irreg_conj then table.insert(ann_parts, "irregular") end forms.annotation = " (" .. table.concat(ann_parts, ", ") .. ")"

if alternant_multiword_spec.aspect == "pf" then forms.aspect_indicator = "perfective aspect" elseif alternant_multiword_spec.aspect == "impf" then forms.aspect_indicator = "imperfective aspect" else forms.aspect_indicator = "biaspectual" end

forms.notes_clause = forms.footnote ~= "" and m_string_utilities.format(notes_template, forms) or "" forms.present_table = m_string_utilities.format(		alternant_multiword_spec.aspect == "both" and table_spec_biaspectual_present or table_spec_single_aspect_present,		forms	) local table_spec_indicative_header = table_spec_person_number_header:gsub("MOOD", "indicative") forms.indicative_header = m_string_utilities.format(table_spec_indicative_header, forms) local table_spec_conditional_header = table_spec_person_number_header:gsub("MOOD", "conditional") forms.conditional_header = m_string_utilities.format(table_spec_conditional_header, forms) local table_spec_past = table_spec_person_number_gender:gsub("TENSE", "past"):gsub("PREF", "past") forms.past_table = m_string_utilities.format(table_spec_past, forms) local table_spec_cond = table_spec_person_number_gender:gsub("TENSE", "present"):gsub("PREF", "cond") forms.cond_table = m_string_utilities.format(table_spec_cond, forms) local table_spec_cond_past = table_spec_person_number_gender:gsub("TENSE", "past"):gsub("PREF", "cond_past") forms.cond_past_table = m_string_utilities.format(table_spec_cond_past, forms) return m_string_utilities.format(table_spec_overall, forms) end

-- Externally callable function to parse and conjugate a verb given user-specified arguments. Return value is -- ALTERNANT_MULTIWORD_SPEC, an object where the conjugated forms are in `ALTERNANT_MULTIWORD_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, from_headword) local params = { [1] = {required = true, default = "jmenovat"}, title = {}, pagename = {}, json = {type = "boolean"}, pos = {}, }

local args = m_para.process(parent_args, params)

-- Ensure we don't split a reflexive verb by replacing the space before 'se' or 'si' with an underscore. local function split_bracketed_runs_into_words(bracketed_runs) for j, segment in ipairs(bracketed_runs) do			if j % 2 == 1 then bracketed_runs[j] = segment:gsub(" (s[ei])$", "_%1") end end return iut.default_split_bracketed_runs_into_words(bracketed_runs) end

local parse_props = { parse_indicator_spec = parse_indicator_spec, split_bracketed_runs_into_words = split_bracketed_runs_into_words, angle_brackets_omittable = true, allow_blank_lemma = true, }	local alternant_multiword_spec = iut.parse_inflected_text(args[1], parse_props) alternant_multiword_spec.title = args.title alternant_multiword_spec.args = args local pagename = args.pagename or mw.title.getCurrentTitle.subpageText alternant_multiword_spec.forms = {} normalize_all_lemmas(alternant_multiword_spec, pagename) detect_all_indicator_specs(alternant_multiword_spec) local inflect_props = { slot_table = verb_slots, lang = lang, inflect_word_spec = conjugate_verb, -- We add links around the generated verbal forms rather than allow the entire multiword -- expression to be a link, so ensure that user-specified links get included as well. include_user_specified_links = true, }	iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- process_overrides(alternant_multiword_spec.forms, args) add_categories(alternant_multiword_spec) return alternant_multiword_spec end

-- Entry point for. 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 alternant_multiword_spec = export.do_generate_forms(parent_args) show_forms(alternant_multiword_spec) return make_table(alternant_multiword_spec) .. require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang) end

return export