Module:zlw-ocs-adjective

local export = {}

--[=[

Authorship: Zhnka

]=]

--[=[

TERMINOLOGY:

-- "slot" = A particular combination of case/gender/number. Example slot names for adjectives are "gen_f" (genitive feminine singular) and "nom_mp" (animate nominative masculine plural). Each slot is filled with zero or more forms.

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

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

local lang = require("Module:languages").getByCode("zlw-ocs") local m_links = require("Module:links") local m_table = require("Module:table") local m_string_utilities = require("Module:string utilities") local iut = require("Module:inflection utilities") local com = require("Module:zlw-ocs-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

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

-- All slots that are used by any of the different tables. The key is the slot and the value is a list of the tables -- that use the slot. "" = regular, "plonly" = special=plonly in, "dva" = special=dva in --. local input_adjective_slots = { nom_m = {""}, nom_f = {""}, nom_n = {""}, nom_md = {"", "duonly"}, nom_fnd = {"", "duonly"}, nom_mp = {"", "plonly"}, nom_fp = {"", "plonly"}, nom_np = {"", "plonly"}, gen_mn = {""}, gen_f = {""}, gen_d = {"", "duonly"}, gen_p = {"", "plonly"}, dat_mn = {""}, dat_f = {""}, dat_d = {"", "duonly"}, dat_p = {"", "plonly"}, acc_m_an = {""}, acc_m_in = {""}, acc_f = {""}, acc_n = {""}, acc_md = {"", "duonly"}, acc_fnd = {"", "duonly"}, acc_mfp = {"", "plonly"}, acc_np = {"", "plonly"}, ins_mn = {""}, ins_f = {""}, ins_d = {"", "duonly"}, ins_p = {"", "plonly"}, loc_mn = {""}, loc_f = {""}, loc_d = {"", "duonly"}, loc_p = {"", "plonly"}, }

local output_adjective_slots = { nom_m = "nom|m|s", nom_f = "nom|f|s", nom_n = "nom|n|s", nom_md = "nom|m|d", nom_fnd = "nom|f//n|d", nom_mp = "nom|m|p", nom_fp = "nom|f|p", nom_np = "nom|n|p", nom_mp = "nom|m|p", nom_fnp = "nom|f//n|p", gen_mn = "gen|m//n|s", gen_f = "gen|f|s", gen_d = "gen|d", gen_p = "gen|p", dat_mn = "dat|m//n|s", dat_f = "dat|f|s", dat_d = "dat|d", dat_p = "dat|p", acc_m_an = "an|acc|m|s", acc_m_in = "in|acc|m|s", acc_f = "acc|f|s", acc_n = "acc|n|s", acc_md = "acc|m|d", acc_fnd = "acc|f//n|d", acc_mfp = "acc|m//f|p", acc_np = "acc|n|p", acc_mp = "acc|m|p", acc_fnp = "acc|f//n|p", ins_mn = "ins|m//n|s", ins_f = "ins|f|s", ins_d = "ins|d", ins_p = "ins|p", loc_mn = "loc|m//n|s", loc_f = "loc|f|s", loc_d = "loc|d", loc_p = "loc|p", }

local function get_output_adjective_slots(alternant_multiword_spec) return output_adjective_slots end

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

local function add(base, slot, stems, endings, footnote) if stems then stems = iut.combine_form_and_footnotes(stems, footnote) end iut.add_forms(base.forms, slot, stems, endings, combine_stem_ending) end

local function add_normal_decl(base, stems,	nom_m, nom_f, nom_n, nom_md, nom_fnd, nom_mp, nom_fp, nom_np,	gen_mn, gen_f, gen_d, gen_p,	dat_mn, dat_f, dat_d, dat_p,	acc_f,	loc_mn, loc_f, loc_d, loc_p,	ins_mn, ins_f, ins_d, ins_p,	footnote) if stems then stems = iut.combine_form_and_footnotes(stems, footnote) end	add(base, "nom_m", stems, nom_m) add(base, "nom_f", stems, nom_f) add(base, "nom_n", stems, nom_n) add(base, "nom_md", stems, nom_md) add(base, "nom_fnd", stems, nom_fnd) add(base, "nom_mp", stems, nom_mp) add(base, "nom_fp", stems, nom_fp) add(base, "nom_np", stems, nom_np) add(base, "gen_mn", stems, gen_mn) add(base, "gen_f", stems, gen_f) add(base, "gen_d", stems, gen_d) add(base, "gen_p", stems, gen_p) add(base, "dat_mn", stems, dat_mn) add(base, "dat_f", stems, dat_f) add(base, "dat_d", stems, dat_d) add(base, "dat_p", stems, dat_p) add(base, "acc_f", stems, acc_f) add(base, "loc_mn", stems, loc_mn) add(base, "loc_f", stems, loc_f) add(base, "loc_d", stems, loc_d) add(base, "loc_p", stems, loc_p) add(base, "ins_mn", stems, ins_mn) add(base, "ins_f", stems, ins_f) add(base, "ins_d", stems, ins_d) add(base, "ins_p", stems, ins_p) end

local decls = {}

decls["normal"] = function(base) local stem, suffix

-- hard in -ý stem, suffix = rmatch(base.lemma, "^(.*)(ý)$") if stem then if vowel_alt ~= nil then add_normal_decl(base, com.apply_second_palatalization(vowel_alt, "is adj"), nil, nil, nil, nil, nil, "í") if not rfind(base.lemma, ".*lý$") then add_normal_decl(base, com.apply_second_palatalization(vowel_alt, "is adj"), nil, nil, nil, nil, "iej", nil, nil, nil, nil, nil, nil, nil, nil, "iej", nil, nil, nil, "iem", "iej") else add_normal_decl(base, vowel_alt, nil, nil, nil, nil, "éj", nil, nil, nil, nil, nil, nil, nil, nil, "éj", nil, nil, nil, "ém", "éj") end add_normal_decl(base, stem,			"ý", "á", "é", "á", "éj", {}, "é", "á",			"ého", "é", "ú", "ých",			"ému", "éj", "ýma", "ým",			"ú",			"ém", "éj", "ú", "ých",			"ým", "ú", "ýma", "ými"		) return else add_normal_decl(base, com.apply_second_palatalization(stem, "is adj"), nil, nil, nil, nil, nil, "í") if not rfind(base.lemma, ".*lý$") then add_normal_decl(base, com.apply_second_palatalization(stem, "is adj"), nil, nil, nil, nil, "iej", nil, nil, nil, nil, nil, nil, nil, nil, "iej", nil, nil, nil, "iem", "iej") end add_normal_decl(base, stem,			"ý", "á", "é", "á", "éj", {}, "é", "á",			"ého", "é", "ú", "ých",			"ému", "éj", "ýma", "ým",			"ú",			"ém", "éj", "ú", "ých",			"ým", "ú", "ýma", "ými"		) return end end if base.comp then stem, suffix = rmatch(base.lemma, "(.*)(í)$") if stem then add_normal_decl(base, stem,			"í", "ši", "še", "še", "še", "še", "še", "še",			"šě", "šě", nil, nil,			"šu", "ši", nil, nil,			"šu",			"ši", "ši", nil, nil,			"šem", "šú") return end else -- soft in -í stem, suffix = rmatch(base.lemma, "^(.*)(í)$") if stem then add_normal_decl(base, stem,			"í", {}, {}, {}, "í", "í", {}, {},			{}, {}, {}, "ích",			{}, "í", "íma", "ím",			{},			{}, "í", {}, "ích",			"ím", {}, "íma", "ími"		) add_normal_decl(base, com.convert_paired_plain_to_palatal(stem, ending), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, "ú", nil, nil, nil, nil, nil, "ú", nil, nil, "ú", nil, nil, "ú") if rfind(base.lemma, ".*lí$") then add_normal_decl(base, stem, nil, "é", "é", "é", "éj", nil, "é", "é", "ého", "é", nil, nil, "ému", "éj", nil, nil, nil, "ém", "éj") else add_normal_decl(base, stem, nil, "ie", "ie", "ie", "iej", nil, "ie", "ie", "ieho", "ie", nil, nil, "iemu", "iej", nil, nil, nil, "iem", "iej") end

return end -- possessive in -óv stem, suffix = rmatch(base.lemma, "^(.*)(óv)$") if stem then add_normal_decl(base, stem,			"óv", "ova", "ovo", "ova", "ově", "ovi", "ovy", "ova",			"ova", "ovy", "ovú", "ových",			"ovu", "ově", "ovýma", "ovým",			"ovu",			{"ově", "ovu"}, "ově", "ovú", "ových",			"ovým", "ovú", "ovýma", "ovými"		) return end

-- possessive in -in stem, suffix = rmatch(base.lemma, "^(.*)(in)$") if stem then add_normal_decl(base, stem,			"in", "ina", "ino", "ina", "ině", "ini", "iny", "ina",			"ina", "iny", "inú", "iných",			"inu", "ině", "inýma", "iným",			"inu",			{"ině", "inu"}, "ině", "inú", "iných",			"iným", "inú", "inýma", "inými"		) return end

if base.num then stem, suffix = rmatch(base.lemma, "^(.*)" .. com.inherently_soft_c) if not stem then add_normal_decl(base, base.lemma, "") add_normal_decl(base, base.red == true and com.reduce(base.lemma) or base.lemma,			nil, "a", "o", nil, nil, {}, "y", "a",			"a", "y", nil, "ých",			"u", {}, nil, "ým",			"u",			{}, {}, nil, "ých",			"em", "ú", nil, "ými"		) add_normal_decl(base, base.red == true and com.reduce( com.apply_second_palatalization(base.lemma, "is adj")) or com.apply_second_palatalization(base.lemma, "is adj"), nil, nil, nil, nil, nil, "i", nil, nil, nil, nil, nil, nil, nil, "ě", nil, nil, nil, "ě", "ě") return end stem, suffix = rmatch(base.lemma, "^(.*)(ój)$") if stem then add_normal_decl(base, stem,			"ój", "ojě", {"oje", "é"}, "ojě", "oji", "oji", "ojě", "ojě",			{"ojeho", "ého"}, "ojie", "ojú", {"ojich", "ých"},			{"ojemu", "ému"}, "ojí", {"ojima", "ýma"}, {"ojim", "ým"},			"oju",			{"ojem", "ém"}, "ojí", "ojú", {"ojim", "ých"},			{"ojím", "ým"}, "ojú", {"ojima", "ýma"}, {"ojimi", "ými"}		) return end end

-- short soft stem, suffix = rmatch(base.lemma, "^(.*)" .. com.inherently_soft_c .. "$") if stem then add_normal_decl(base, base.lemma,			"", {}, "e", {}, {}, {}, {}, {},			{}, {}, nil, nil,			"u", {}, nil, nil,			"u",			nil, nil, nil, nil,			nil, nil, nil, nil		) add_normal_decl(base, com.convert_paired_palatal_to_plain(base.lemma, ending), nil, "ě", nil, "ě", "i", "i", "ě", "ě", "ě", "ě", nil, nil, nil, "i", nil, nil, nil, nil, nil, nil, nil, nil, nil) return end -- short hard stem, suffix = rmatch(base.lemma, "^(.*)" .. com.cons_c) if stem then add_normal_decl(base, base.lemma, "") add_normal_decl(base, base.red == true and com.reduce(base.lemma) or base.lemma,			nil, "a", "o", "a", {}, {}, "y", "a",			"a", "y", nil, nil,			"u", {}, nil, nil,			"u",			{}, {}, nil, nil,			"em", "ú", nil, nil		) add_normal_decl(base, base.red == true and com.reduce( com.apply_second_palatalization(base.lemma, "is adj")) or com.apply_second_palatalization(base.lemma, "is adj"), nil, nil, nil, nil, "ě", "i", nil, nil, nil, nil, nil, nil, nil, "ě", nil, nil, nil, "ě", "ě") return end end error("Unrecognized adjective lemma, should end in '-ý', '-í', '-ův' or '-in': '" .. base.lemma .. "'") end

decls["irreg"] = function(base) local stem, suffix

-- determiner like mój stem, suffix = rmatch(base.lemma, "^(.*)(ój)$") if stem then add_normal_decl(base, stem,			"ój", {"á", "ojě"}, {"é", "oje"}, "á", "oji", "oji", {"é", "ojě"}, {"á", "ojě"},			"ého", "é", {"ú", "ojú"}, "ých",			"ému", {"ej", "éj"}, "ýma", "ým",			{"ú", "oju"},			"ém", {"ej", "éj"}, {"ú", "ojú"}, "ých",			"ým", "ú", "ýma", "ými"		) return end

if base.lemma == "veš" then add_normal_decl(base, "",			"veš", "všě", "vše", nil, nil, "vši", "všě", "všě",			"všeho", "všie", nil, "všěch",			"všemu", {"vší", "všiej"}, nil, "všěm",			"všu",			"všem", {"vší", "všiej"}, nil, "všěch",			"všiem", "všú", nil, "všěmi"		) return end

if base.lemma == "sen" then add_normal_decl(base, "",			"sen", "sie", "se", nil, nil, "si", nil, nil,			"seho", "sie", nil, "sich",			"semu", {"sí", "siej"}, "sima", "sim",			"śú",			"sem", {"sí", "siej"}, nil, "sich",			"sím", "śú", "sima", "simi"		) return end

-- determiner like ten, tento, onen, jeden stem, suffix = rmatch(base.lemma, "^(.*)(en)$") if stem then local nom_stem = stem .. suffix if nom_stem == "jeden" then stem = "jedn" end add_normal_decl(base, nom_stem, "") add_normal_decl(base, stem,			nil, "a", "o", "a", "ě", "i", "y", "a",			"oho", "é", "ú", "ěch",			"omu", {"ej", "éj"}, "ěma", "ěm",			"u",			"om", {"ej", "éj"}, "ú", "ěch",			"iem", "ú", "ěma", "ěmi"		) return end

-- náš, váš stem, suffix = rmatch(base.lemma, "^(.*)(áš)$") if stem then local nom_stem = stem .. suffix stem = stem .. "aš" add_normal_decl(base, nom_stem,			"", "ě", "e", "ě", "i", "i", "ě", "ě",			"eho", "ie", "ú", "ich",			"emu", {"í", "iej"}, "ima", "im",			"u",			"em", {"í", "iej"}, "ú", "ich",			"ím", "ú", "ima", "imi" ) add_normal_decl(base, stem,			nil, "ě", "e", "ě", "i", "i", "ě", "ě",			"eho", "ie", "ú", "ich",			"emu", {"í", "iej"}, "ima", "im",			"u",			"em", {"í", "iej"}, "ú", "ich",			"ím", "ú", "ima", "imi"		) return end

if base.lemma == "jenž" then local preposition_footnote = "the leading letter j- is changed to n- when the pronoun is preceded by a preposition, e.g., , " preposition_footnote = "[" .. mw.getCurrentFrame:preprocess(preposition_footnote) .. "]"		-- Add the non-prepositional forms. add_normal_decl(base, "",			"jenž", "jěž", "jež", "jěž", "již", "již", "jěž", "jěž",			"jehož", "jiež", "júž", "jichž",			"jemuž", {"jíž", "jiejž"}, "jimaž", "jimž",			"juž",			nil, nil, nil, nil,			"jímž", "júž", "jimaž", "jimiž"		) -- Add the prepositional forms. (FIXME: Maybe should go in a separate column in a special table.) add_normal_decl(base, "",			nil, nil, nil, nil, nil, nil, nil, nil,			"ňehož", "niež", "ňúž", "nichž",			"ňemuž", {"níž", "niejž"}, "nimaž", "nimž",			"ňuž",			"ňemž", {"níž", "niejž"}, "ňúž", "nichž",			"nímž", "ňúž", "nimaž", "nimiž",			preposition_footnote		) add(base, "acc_m_an", "", {"jejž", "jenž", "již", "jehož"}) add(base, "acc_m_an", "", {"ňejž", "ňenž", "niž", "něhož"}, preposition_footnote) return end

if base.lemma == "sám" then -- This mixes long and short endings. add_normal_decl(base, "sám", "") add_normal_decl(base, "sam",			nil, "a", "o", "a", "ě", "i", "y", "a",			{"oho", "a"}, "é", "ú", "ěch",			{"omu", "u"}, {"ej", "éj"}, "ěma", "ěm",			"u",			"om", {"ej", "éj"}, "ú", "ěch",			"iem", "ú", "ěma", "ěmi"		) return end

error("Unrecognized irregular lemma '" .. base.lemma .. "'") 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 base = {forms = {}} if inside ~= "" then local parts = rsplit(inside, ".", true) for _, part in ipairs(parts) do			if part == "irreg" then base.irreg = true elseif part == "*" then base.red = true elseif part == "comp" then base.comp = true elseif part == "num" then base.num = true else error("Unrecognized indicator '" .. part .. "': '" .. inside .. "'") 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)		base.lemma = base.orig_lemma_no_links	end) end

local function detect_indicator_spec(base) if base.irreg then base.decl = "irreg" else base.decl = "normal" end end

local function detect_all_indicator_specs(alternant_multiword_spec) iut.map_word_specs(alternant_multiword_spec, function(base)		detect_indicator_spec(base)	end) end

local function decline_adjective(base) if not decls[base.decl] then error("Internal error: Unrecognized declension type '" .. base.decl .. "'") end decls[base.decl](base) -- handle_derived_slots_and_overrides(base) end

-- Process override for the arguments in `args`, storing the results into `forms`. If `do_acc`, only do accusative -- slots; otherwise, don't do accusative slots. local function process_overrides(forms, args, do_acc) for slot, _ in pairs(input_adjective_slots) do		if args[slot] and not not do_acc == not not slot:find("^acc") then forms[slot] = nil if args[slot] ~= "-" and args[slot] ~= "—" then local segments = iut.parse_balanced_segment_run(args[slot], "[", "]") local comma_separated_groups = iut.split_alternating_runs(segments, "%s*,%s*") for _, comma_separated_group in ipairs(comma_separated_groups) do					local formobj = { form = comma_separated_group[1], footnotes = fetch_footnotes(comma_separated_group), }					iut.insert_form(forms, slot, formobj) end end end end end

local function check_allowed_overrides(alternant_multiword_spec, args) local special = alternant_multiword_spec.special or alternant_multiword_spec.surname and "surname" or "" for slot, types in pairs(input_adjective_slots) do		if args[slot] then local allowed = false for _, typ in ipairs(types) do				if typ == special then allowed = true break end end if not allowed then error(("Override %s= not allowed for %s"):format(slot, special == "" and "regular declension" or "special=" .. special)) end end end end

local function set_accusative(alternant_multiword_spec) local forms = alternant_multiword_spec.forms local function copy_if(from_slot, to_slot) if not forms[to_slot] then iut.insert_forms(forms, to_slot, forms[from_slot]) end end

copy_if("nom_n", "acc_n") copy_if("gen_mn", "acc_m_an") copy_if("nom_m", "acc_m_in") copy_if("nom_fp", "acc_mfp") copy_if("nom_np", "acc_np") copy_if("nom_md", "acc_md") copy_if("nom_fnd", "acc_fnd") end

local function add_categories(alternant_multiword_spec) local cats = {} local plpos = m_string_utilities.pluralize(alternant_multiword_spec.pos or "adjective") local function insert(cattype) m_table.insertIfNot(cats, "Old Czech " .. cattype .. " " .. plpos) end if not alternant_multiword_spec.manual then iut.map_word_specs(alternant_multiword_spec, function(base)			if base.decl == "irreg" then				insert("irregular")			elseif rfind(base.lemma, "ý$") then				insert("hard")			elseif rfind(base.lemma, "í$") then				insert("soft")			elseif base.num then				insert("number")			elseif rfind(base.lemma, "óv$") then				insert("possessive")			elseif rfind(base.lemma, "in$") then				insert("possessive")			elseif rfind(base.lemma, "^(.*)" .. com.inherently_soft_c .. "$") then				insert("short soft")			elseif rfind(base.lemma, "^(.*)" .. com.cons_c .. "$") then				insert("short hard")			end		end) end alternant_multiword_spec.categories = cats end

local function show_forms(alternant_multiword_spec) local lemmas = {} local lemmaform = alternant_multiword_spec.forms.nom_m or alternant_multiword_spec.forms.nom_mp if lemmaform then for _, form in ipairs(lemmaform) do			table.insert(lemmas, form.form) end end local props = { lemmas = lemmas, slot_table = get_output_adjective_slots(alternant_multiword_spec), 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 function template_prelude(min_width) return rsub([===[ {title}{annotation} {\op}| border="1px solid #000000" style="border-collapse:collapse;background:#F9F9F9;text-align:center; min-width:MINWIDTHem" class="inflection-table" ]===], "MINWIDTH", min_width) end

local function template_postlude return [=[ end
 * {\cl}{notes_clause}  ]=]

local table_spec_sg = [=[ ! style="background:#d9ebff" colspan=5 | singular ! style="background:#d9ebff" | ! style="background:#d9ebff" | masculine ! style="background:#d9ebff" | feminine ! style="background:#d9ebff" | neuter ! style="background:#eff7ff" | nominative ! style="background:#eff7ff" | genitive ! style="background:#eff7ff" | dative ! style="background:#eff7ff" | accusative ! style="background:#eff7ff" | locative ! style="background:#eff7ff" | instrumental ]=]
 * {nom_m}
 * {nom_f}
 * {nom_n}
 * {gen_mn}
 * {gen_f}
 * {gen_mn}
 * {dat_mn}
 * {dat_f}
 * {dat_mn}
 * {acc_m_an}, {acc_m_in}
 * {acc_f}
 * {acc_n}
 * {loc_mn}
 * {loc_f}
 * {loc_mn}
 * {ins_mn}
 * {ins_f}
 * {ins_mn}

local table_spec_du = [=[ ! style="background:#d9ebff" colspan=5 | dual ! style="background:#d9ebff" | ! style="background:#d9ebff" | masculine ! style="background:#d9ebff" | feminine ! style="background:#d9ebff" | neuter ! style="background:#eff7ff" | nominative ! style="background:#eff7ff" | genitive ! style="background:#eff7ff" | dative ! style="background:#eff7ff" | accusative ! style="background:#eff7ff" | locative ! style="background:#eff7ff" | instrumental ]=]
 * {nom_md}
 * colspan=2 | {nom_fnd}
 * colspan=3 | {gen_d}
 * colspan=3 | {dat_d}
 * {acc_md}
 * colspan=2 | {acc_fnd}
 * colspan=3 | {loc_d}
 * colspan=3 | {ins_d}

local table_spec_pl = [=[ ! style="background:#d9ebff" colspan=5 | plural ! style="background:#d9ebff" | ! style="background:#d9ebff" | masculine ! style="background:#d9ebff" | feminine ! style="background:#d9ebff" | neuter ! style="background:#eff7ff" | nominative ! style="background:#eff7ff" | genitive ! style="background:#eff7ff" | dative ! style="background:#eff7ff" | accusative ! style="background:#eff7ff" | locative ! style="background:#eff7ff" | instrumental ]=]
 * {nom_mp}
 * {nom_fp}
 * {nom_np}
 * colspan=3 | {gen_p}
 * colspan=3 | {dat_p}
 * colspan=2 | {acc_mfp}
 * {acc_np}
 * colspan=3 | {loc_p}
 * colspan=3 | {ins_p}

local table_spec = template_prelude("55") .. table_spec_sg .. "|-\n" .. table_spec_du .. "|-\n" .. table_spec_pl .. template_postlude

local table_spec_plonly = template_prelude("55") .. table_spec_pl .. template_postlude local table_spec_nodual = template_prelude("55") .. table_spec_sg .. "|-\n" .. table_spec_pl .. template_postlude

local table_spec_duonly = template_prelude("40") .. [=[ ! style="width:40%;background:#eff7ff" colspan="2" | ! style="background:#eff7ff" colspan="2" | dual ! style="width:40%;background:#eff7ff" colspan="2" | ! style="background:#eff7ff" | masculine ! style="background:#eff7ff" | feminine/neuter ! style="background:#eff7ff" colspan="2" | nominative ! style="background:#eff7ff" colspan="2" | genitive ! style="background:#eff7ff" colspan="2" | dative ! style="background:#eff7ff" colspan="2" | accusative ! style="background:#eff7ff" colspan="2" | locative ! style="background:#eff7ff" colspan="2" | instrumental ]=] .. template_postlude
 * {nom_md}
 * {nom_fnd}
 * colspan="2" | {gen_d}
 * colspan="2" | {dat_d}
 * {acc_md}
 * {acc_fnd}
 * colspan="2" | {loc_d}
 * colspan="2" | {ins_d}

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

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

if alternant_multiword_spec.manual then forms.annotation = "" else local ann_parts = {} local decls = {} iut.map_word_specs(alternant_multiword_spec, function(base)				if base.decl == "irreg" then				m_table.insertIfNot(decls,"irregular")			elseif rfind(base.lemma, "ý$") then				m_table.insertIfNot(decls, "hard")			elseif base.comp then				m_table.insertIfNot(decls, "short comparative")			elseif rfind(base.lemma, "í$") then				m_table.insertIfNot(decls, "soft")			elseif base.num then				m_table.insertIfNot(decls, "number")			elseif rfind(base.lemma, "óv$") then				m_table.insertIfNot(decls, "possessive")			elseif rfind(base.lemma, "in$") then				m_table.insertIfNot(decls, "possessive")			elseif rfind(base.lemma, "^(.*)" .. com.inherently_soft_c .. "$") then				m_table.insertIfNot(decls, "short soft")			elseif rfind(base.lemma, "^(.*)" .. com.cons_c .. "$") then				m_table.insertIfNot(decls, "short hard")			end		end) table.insert(ann_parts, table.concat(decls, " // ")) forms.annotation = " (" .. table.concat(ann_parts, ", ") .. ")" end

forms.notes_clause = forms.footnote ~= "" and m_string_utilities.format(notes_template, forms) or "" return m_string_utilities.format(		alternant_multiword_spec.special == "plonly" and table_spec_plonly or		alternant_multiword_spec.special == "duonly" and table_spec_duonly or		alternant_multiword_spec.special == "nodual" and table_spec_nodual or		table_spec, forms	) end

-- Externally callable function to parse and decline an adjective given -- user-specified arguments. Return value is WORD_SPEC, an object where the -- declined forms are in `WORD_SPEC.forms` for each slot. If there are no values -- for a slot, the slot key will be missing. The value for a given slot is a -- list of objects {form=FORM, footnotes=FOOTNOTES}. function export.do_generate_forms(parent_args, pos, from_headword, def) local params = { [1] = {},		vowel_alt = {}, pos = {}, json = {type = "boolean"}, -- for use with bots title = {}, pagename = {}, }	for slot, _ in pairs(input_adjective_slots) do		params[slot] = {} end

-- Only default param 1 when displaying the template. local args = require("Module:parameters").process(parent_args, params) local SUBPAGE = mw.title.getCurrentTitle.subpageText local pagename = args.pagename or SUBPAGE vowel_alt = args.vowel_alt if not args[1] then if SUBPAGE == "zlw-ocs-adecl" then args[1] = "měkký" else args[1] = pagename end end local parse_props = { parse_indicator_spec = parse_indicator_spec, allow_default_indicator = true, allow_blank_lemma = true, }	local alternant_multiword_spec = iut.parse_inflected_text(args[1], parse_props) alternant_multiword_spec.pos = args.pos alternant_multiword_spec.title = args.title alternant_multiword_spec.forms = {} normalize_all_lemmas(alternant_multiword_spec, pagename) detect_all_indicator_specs(alternant_multiword_spec) check_allowed_overrides(alternant_multiword_spec, args) local inflect_props = { slot_table = get_output_adjective_slots(alternant_multiword_spec), inflect_word_spec = decline_adjective, }	iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- Do non-accusative overrides so they get copied to the accusative forms appropriately. process_overrides(alternant_multiword_spec.forms, args) set_accusative(alternant_multiword_spec) -- Do accusative overrides after copying the accusative forms. process_overrides(alternant_multiword_spec.forms, args, "do acc") add_categories(alternant_multiword_spec) if args.json and not from_headword then return require("Module:JSON").toJSON(alternant_multiword_spec) end return alternant_multiword_spec end

-- Externally callable function to parse and decline an adjective where all -- forms are given manually. Return value is WORD_SPEC, an object where the -- declined forms are in `WORD_SPEC.forms` for each slot. If there are no values -- for a slot, the slot key will be missing. The value for a given slot is a -- list of objects {form=FORM, footnotes=FOOTNOTES}. function export.do_generate_forms_manual(parent_args, pos, from_headword, def) local params = { pos = {}, special = {}, json = {type = "boolean"}, -- for use with bots title = {}, }	for slot, _ in pairs(input_adjective_slots) do		params[slot] = {} end

local args = require("Module:parameters").process(parent_args, params) local alternant_multiword_spec = { pos = args.pos, special = args.special, title = args.title, forms = {}, manual = true, }	check_allowed_overrides(alternant_multiword_spec, args) -- Do non-accusative overrides so they get copied to the accusative forms appropriately. process_overrides(alternant_multiword_spec.forms, args) set_accusative(alternant_multiword_spec) -- Do accusative overrides after copying the accusative forms. process_overrides(alternant_multiword_spec.forms, args, "do acc") add_categories(alternant_multiword_spec) if args.json and not from_headword then return require("Module:JSON").toJSON(alternant_multiword_spec) end return alternant_multiword_spec end

-- Entry point for. Template-callable function to parse and decline -- an adjective given user-specified arguments and generate a displayable table -- of the declined 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

-- Entry point for. Template-callable function to parse and -- decline an adjective given manually-specified inflections and generate a -- displayable table of the declined forms. function export.show_manual(frame) local parent_args = frame:getParent.args local alternant_multiword_spec = export.do_generate_forms_manual(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