Module:Pl-noun

local export = {}

local m_links = require('Module:links') local m_adj = require('Module:pl-adj') local m_g = require('Module:gender and number') local lang = require("Module:languages").getByCode("pl")

-- local consonants = "[bcćdfghjklłmnńpqrsśtvwxzżź]";

-- case information local cases = { { key = "nom"; en = "nominative"; pl = "mianownik (kto? co?)" }, { key = "gen"; en = "genitive"; pl = "dopełniacz (kogo? czego?)" }, { key = "dat"; en = "dative"; pl = "celownik (komu? czemu?)" }, { key = "acc"; en = "accusative"; pl = "biernik (kogo? co?)" }, { key = "ins"; en = "instrumental"; pl = "narzędnik (kim? czym?)" }, { key = "loc"; en = "locative"; pl = "miejscownik (o kim? o czym?)" }, { key = "voc"; en = "vocative"; pl = "wołacz (o!)" }, }

-- columns for normal nouns local noun_cols = { { key = "s"; title = "singular" }, { key = "p"; title = "plural" }, }

-- columns for Old Polish nouns local noun_dual_cols = { { key = "s"; title = "singular" }, { key = "d"; title = "dual" }, { key = "p"; title = "plural" }, }

-- columns for pronouns local pronoun_cols = { { key = "sm"; title = m_g.format_list({"m"}, lang) }, { key = "sf"; title = m_g.format_list({"f"}, lang) }, { key = "sn"; title = m_g.format_list({"n"}, lang) }, { key = "pm"; title = m_g.format_list({"m-pr-p"}, lang) }, { key = "po"; title = m_g.format_list({"np-p"}, lang) }, }

local altsep = "/"

function empty_item(text) return (not text) or text == "" or text == "-" or text == "–" or text == "—" end

-- add link markers, with links separated by any of splitchars -- exported for use in testcases -- normally the separator is altsep function export.make_links(text, splitchars, title) if not title then title = mw.title.getCurrentTitle.fullText end if empty_item(text) then return "—" elseif not splitchars or splitchars == "" then return ("%s"):format(text, text) else items = {} for word in mw.ustring.gmatch(text, "[^" .. splitchars .. "]+") do			if word == title then table.insert(items, ("%s"):format(word)) else table.insert(items, ("%s"):format(word, word)) end end if #items > 1 then require("Module:debug").track("pl-noun/splitchars") end return table.concat(items, "/") end end

local function linkify_info(declinfo, splitchars, nolinks) if nolinks then require("Module:debug").track("pl-noun/nolinks") end local linked = {} local title = mw.title.getCurrentTitle.fullText for k, v in pairs(declinfo) do		if v == title then linked[k] = "" .. v .. "" elseif nolinks then linked[k] = v		else linked[k] = export.make_links(v, splitchars, title) end end return linked end

local function nowiki_info(declinfo) require("Module:debug").track("pl-noun/nowiki") local nowikied = {} for k, v in pairs(declinfo) do		nowikied[k] = mw.text.nowiki(v) end return nowikied end

local function override_col_titles(heads, cols) if not heads then return cols or {} end local new_cols = {} local index = 1 for word in mw.ustring.gmatch(heads, "[^,]+") do		if cols[index] then table.insert(new_cols, { key = cols[index].key; title = word } ) else table.insert(new_cols, { key = tostring(index); title = word } ) end index = index + 1 end for ci, col in ipairs(cols) do		if ci >= index then table.insert(new_cols, cols[index]) end end return new_cols end

local function normalize_tantum(pargs) -- support "num" as fallback for "tantum" to match Latin templates local tantum = pargs.tantum or pargs.num if not tantum then return nil end if tantum == "sg" then tantum = "s" elseif tantum == "pl" then tantum = "p" end return tantum end

local function guess_width(declinfo, cols) local maxl = 0 for k, v in pairs(declinfo) do		local l = mw.ustring.len(mw.text.trim(v)) if maxl < l then maxl = l		end end local width = math.floor(maxl * 0.78) -- number obtained by anecdotal evidence width = (width < 10) and 10 or width width = 9 + (width * #cols) return width end

-- generate the HTML code of an inflection table -- each entry in heads must have "key" and "title" local function make_table(declinfo, cols, preproc, width, title, tantum, nolinks) local result = {} if not cols or not cols[1] then error("make_table: invalid cols parameter") end

local lemma_key = cases[1].key .. cols[1].key local lemma = m_links.remove_links(declinfo[lemma_key]) title = title or ('declension of %s '):format(lemma)

local emwidth = width or guess_width(declinfo, cols)

if preproc == "nowiki" then declinfo = nowiki_info(declinfo) elseif preproc == "linkify" then declinfo = linkify_info(declinfo, altsep, nolinks) end

if cols and (#cols > 0) then table.insert(result, '|-\n! style="width: 8em;" |\n') for i, col in ipairs(cols) do table.insert(result, ('! scope="col" | %s\n'):format(col.title)) end end

local maxl = 0 for i, case in ipairs(cases) do table.insert(result, ('|-\n! title="%s" scope="row" | %s\n'):format(case.pl, case.en)) for _, col in ipairs(cols) do local declkey = case.key .. col.key local item = mw.text.trim(declinfo[declkey]) if empty_item(item) then table.insert(result, '| —\n') else table.insert(result, ('| %s \n'):format(item)) end end end

local outtext = ([=[ %s {| style="width: %uem; margin: 0;" class="wikitable inflection-table" ]=]):format(emwidth, title, emwidth) .. table.concat(result, "") .. "|} "

if tantum == "s" then outtext = outtext .. ""	elseif tantum == "p" then outtext = outtext .. ""	end return outtext end

local function get_mode local frame = mw.getCurrentFrame if mw.isSubsting then return 'subst' elseif frame:getParent:getTitle == mw.title.getCurrentTitle.fullText then return 'demo' else return 'xclude' end end

local function make_table_from_pargs(pargs, cols, tantum) local width = pargs.width and tonumber(pargs.width) local declinfo = {} if get_mode == 'demo' then if not cols then cols = { { key = "1"; title = "column 1" }, { key = "2"; title = "column 2" }, { key = "3"; title = "column 3" }, }		end for i = 1, 7 do			for j, col in ipairs(cols) do local case_key = cases[i].key .. col.key local argn = (i-1) * #cols + j				declinfo[case_key] = '' end end return make_table(declinfo, cols, "nowiki", width, nil, nil, nil) else cols = override_col_titles(pargs.heads, cols or {}) for i = 1, 7 do			for j, col in ipairs(cols) do local case_key = cases[i].key .. col.key local argn = ((i-1) * #cols) + j				declinfo[case_key] = m_links.remove_links(mw.text.trim(pargs[case_key] or pargs[argn] or "-")) end end return make_table(declinfo, cols, "linkify", pargs.width, pargs.title, normalize_tantum(pargs), pargs.nolinks) end end

-- Generate declension table for a singulare tantum with all forms passed explicitly as parameters function export.template_decl_noun_sg(frame) local pargs = frame:getParent.args local cols = { noun_cols[1] } return make_table_from_pargs(pargs, cols, "s") end

-- Generate declension table for a plurale tantum with all forms passed explicitly as parameters function export.template_decl_noun_pl(frame) local pargs = frame:getParent.args local cols = { noun_cols[2] } return make_table_from_pargs(pargs, cols, "p") end

-- Generate declension table for a regular noun with all forms passed explicitly as parameters function export.template_decl_noun(frame) local pargs = frame:getParent.args return make_table_from_pargs(pargs, noun_cols, nil) end

function export.template_decl_pronoun(frame) local pargs = frame:getParent.args return make_table_from_pargs(pargs, pronoun_cols, nil) end

-- this probably belongs in a module for Old Polish (zlw-opl). -- which reminds me: someone should write WT:AZLW-OPL. -- Generate declension table for a noun with dual number with all forms passed explicitly as parameters function export.template_decl_noun_dual(frame) local pargs = frame:getParent.args return make_table_from_pargs(pargs, noun_dual_cols, nil) end

function export.template_decl_generic(frame) local pargs = frame:getParent.args local heads = pargs.heads if not heads then if get_mode == "demo" then heads = "column 1,column 2" else error("No column headings defined!") end end local cols = override_col_titles(heads, {}) return make_table_from_pargs(pargs, cols, nil) end

-- - -- - -- - Semi-automatic generation of inflected forms -- -- - -- -

local function nonempty(str) if not str or str == "" then return nil else return str end end

-- Generate a table contains true for each space-separated word in str local function make_lookup_table(str) local ret = {} for i in mw.ustring.gmatch(str, "%a+") do		ret[i] = true end return ret end

-- table that converts nominative soft endings to their genitive form -- required for proper functioning of masc_common local soft_ending_lookup = { ["ć"] = "ci"; ["dź"] = "dzi"; ["ń"] = "ni"; ["ś"] = "si"; ["ź"] = "zi"; }

-- Given a word and a lookup table of accepted endings, -- split the word into a stem and an ending. -- Ending is returned in genitive form, i.e. soft consonants -- and digraphs such as ć, ś, dź are converted to midword -- i-forms ci, si, dzi. local function split_stem(word, lookup) -- match the longest possible ending local last, last_gen local limit = math.min(mw.ustring.len(word), 5) for i = -limit, -1 do		if not last_gen then last = mw.ustring.sub(word, i)			if lookup[last] or soft_ending_lookup[last] then last_gen = soft_ending_lookup[last] or last end end end

if last_gen then local stem = mw.ustring.sub(word, 1, -mw.ustring.len(last)-1) return stem, last_gen else return nil, nil end end

local function check_split(stem, last) if not stem then error("nil stem encountered in declension pattern") end if not last then error("nil ending encountered in declension pattern") end end

local function handle_overrides(pargs, declinfo, ovinfo) -- process cases in order local ret = {} local singpl = { "s", "p" } for i = 1, 14 do		local caseno = math.floor((i+1)/2) local sp = 2 - (i % 2) local case_key = cases[caseno].key .. singpl[sp] if pargs[case_key] then ret[case_key] = pargs[case_key] if ovinfo[case_key] then for dummy, v in pairs(ovinfo[case_key]) do					ret[v] = pargs[case_key] end end elseif not ret[case_key] then ret[case_key] = declinfo[case_key] end end return ret end

-- This table will hold patterns local patterns = {}

-- - -- -- Masculine declension - -- -

-- accepted masculine endings in genitive singular form local masc_endings = make_lookup_table(	"acz b bi c ch ci ciec ciel cz d dz dzi dziec ek el eł f g giel h iec ieł ik j k kiel l ł m n ni niec p pi r rz rzeł " ..	"s si seł siec sł sm sn st sz t unek w wi x z zi ż zd zeł ziec zł zm zn")

-- common function for masculine declensions local function masc_common(pattern, stem, last, gens_ending, noms_form, nomp_form, altgenp, explicit_stem) -- verify that the word ending is supported if not masc_endings[last] then -- suppress module error on the template page if not last or not mw.ustring.match(last, "^{{{") then error("Unsupported word ending: " .. (last or "nil")) end end local initial_last = last

-- nominative singular local noms_lookup = { bi = "b"; ci = "ć"; dzi = "dź"; ni = "ń"; ["pi"] = "p"; si = "ś"; wi = "w"; zi = "ź"; } if not noms_form then noms_form = stem .. (noms_lookup[last] or last) end

-- fix the only exceptions to -iec vowel elision if last == "iec" and (stem == "w" or stem == "p") then if not gens_ending and stem == "p" then gens_ending = "a" end stem = stem .. "ie" last = "c" end

-- limited guessing of masculine inanimate endings here -- personal and animate nouns (except for wół) always have "a" local gens_a = make_lookup_table("acz ciec ciel dzi dziec ek el eł iec ieł ik kiel giel ni niec seł siec rz rzeł zeł") if not gens_ending then if gens_a[last] then gens_ending = "a" else gens_ending = "u" end end

-- handle the following things here: --  regular vowel elisions --  overlong endings used only for genitive singular guessing, e.g. acz, unek --  x becoming ks in inflected forms --  stem-final ó becoming o in inflected forms local last_lookup = { acz = "cz"; ciec = "c"; ciel = "l"; dziec = "c"; ek = "k"; el = "l"; ["eł"] = "ł"; iec = "c"; ["ieł"] = "ł"; ik = "k"; kiel = "l"; giel = "l"; niec = "c"; ["rzeł"] = "ł"; ["seł"] = "sł"; siec = "c"; unek = "k"; x = "s"; ["zeł"] = "zł"; ziec = "c"; }	local stem_add = { acz = "a"; ciec = "ć"; ciel = "cie"; dziec = "dź"; ik = "i"; kiel = "k"; giel = "g"; niec = "ń"; ["rzeł"] = "r"; siec = "ś"; unek = "un"; x = "k"; ziec = "ź"; }	stem = stem .. (stem_add[last] or "") last = last_lookup[last] or last

if mw.ustring.match(stem, "ó$") and not explicit_stem then stem = mw.ustring.sub(stem, 1, -2) .. "o" end

-- genitive singular local gens_form = stem .. last .. gens_ending

-- accusative singular local accs_form = (pattern == "m-in") and noms_form or gens_form

-- instrumental singular local inss_form if (last == "g") or (last == "k") then inss_form = stem .. last .. "iem" else inss_form = stem .. last .. "em" end

-- locative singular local locs_lookup = { b = "bie"; bi = "biu"; c = "cu"; ch = "chu"; ci = "ciu"; cz = "czu"; d = "dzie"; dz = "dzu"; dzi = "dziu"; f = "fie"; g = "gu"; h = "hu"; j = "ju"; k = "ku"; l = "lu"; ["ł"] = "le"; m = "mie"; n = "nie"; ni = "niu"; p = "pie"; ["pi"] = "piu"; r = "rze"; rz = "rzu"; s = "sie"; si = "siu"; ["sł"] = "śle"; sm = "śmie"; sn = "śnie"; st = "ście"; sz = "szu"; t = "cie"; w = "wie"; wi = "wiu"; z = "zie"; zd = "ździe"; zi = "ziu"; ["zł"] = "źle"; zm = "zmie"; zn = "źnie"; ["ż"] = "żu"; -- the -zm ending should not be palatalized, since it normally occurs -- in borrowed nouns such as "marazm", "komunizm", "faszyzm" }	local locs_form = stem .. (locs_lookup[last] or last)

-- vocative singular is the same as locative singular, with the exception of -iec local vocs_form = locs_form if pattern == "m-pr" and mw.ustring.match(initial_last, "iec$") then vocs_form = stem .. "cze" end

-- nominative plural -- accusative and vocative plural are the same if not nomp_form then local nomp_e_ending = make_lookup_table("bi c ci cz dz dzi j l ni pi rz si sz wi zi ż") if (last == "g") or (last == "k") then nomp_form = stem .. last .. "i" elseif nomp_e_ending[last] then nomp_form = stem ..last .. "e" else nomp_form = stem .. last .. "y" end end

-- genitive plural local genp_form if mw.ustring.match(last, "i$") and (last ~= "pi") then genp_form = stem .. last elseif (last == "l") then genp_form = stem .. last .. "i" elseif (last == "j") then genp_form = stem .. last .. "ów" if not altgenp or (altgenp == "") then altgenp = stem .. "i" end elseif (last == "cz") or (last == "rz") or (last == "sz") or (last == "ż") then genp_form = stem .. last .. "y" else genp_form = stem .. last .. "ów" end if nonempty(altgenp) then genp_form = genp_form .. "/" .. altgenp end

-- accusative plural - same as genitive or nominative depending on animacy local accp_form = (pattern == "m-pr") and genp_form or nomp_form

return { noms = noms_form;            nomp = nomp_form; gens = gens_form;            genp = genp_form; dats = stem .. last .. "owi"; datp = stem .. last .. "om"; accs = accs_form;            accp = accp_form; inss = inss_form;            insp = stem .. last .. "ami"; locs = locs_form;            locp = stem .. last .. "ach"; vocs = vocs_form;            vocp = nomp_form; } end

-- masculine inanimate nouns, e.g. bruk, beton, słup, szczaw, kurnik -- default genitive singular ending is "u", but "a" endings are common as well patterns["m-in"] = function (pargs, word) local stem, last = split_stem(word, masc_endings) local explicit_stem = false if nonempty(pargs[2]) then stem = pargs[1] or "" last = pargs[2] explicit_stem = true end local gens_ending = pargs[3] or gens_ending local noms_form = nonempty(pargs[4]) local altgenp = nonempty(pargs[5])

local declinfo = masc_common("m-in", stem, last, gens_ending, noms_form,		nil, altgenp, explicit_stem) return handle_overrides(pargs, declinfo,		{ noms = {"accs"}; locs = {"vocs"}; nomp = {"accp", "vocp"} }) end

-- masculine animate nouns, e.g. bocian, dzik, jeleń patterns["m-an"] = function (pargs, word) local stem, last = split_stem(word, masc_endings) local explicit_stem = false if nonempty(pargs[2]) then stem = pargs[1] or "" last = pargs[2] explicit_stem = true end local noms_form = nonempty(pargs[3]) local altgenp = nonempty(pargs[4]) local declinfo = masc_common("m-an", stem, last, "a", noms_form,		nil, altgenp, explicit_stem) return handle_overrides(pargs, declinfo,		{ gens = {"accs"}; locs = {"vocs"}; nomp = {"accp", "vocp"} }) end

-- masculine personal nouns, e.g. policjant, sknerus, polityk patterns["m-pr"] = function (pargs, word) if not pargs[1] and not pargs[2] then if mw.ustring.match(word, "a$") then return patterns["m-pr-a"](pargs, word) elseif mw.ustring.match(word, "[yi]$") then return patterns["m-pr-adj"](pargs, word) elseif mw.ustring.match(word, "nin$") then return patterns["m-pr-nin"](pargs, word) end end local stem, last = split_stem(word, masc_endings) local explicit_stem = false if nonempty(pargs[2]) then stem = pargs[1] or "" last = pargs[2] or "" explicit_stem = true end local nomp_form = nonempty(pargs[3]) or nonempty(pargs.nomp) local noms_form = nonempty(pargs[4]) or nonempty(pargs.noms) local altgenp = nonempty(pargs[5])

-- note: this is only a default, and will need to be overridden often if not nomp_form then nomp_lookup = { acz = "acze"; -- palacz ch = "chowie"; ci = "cie"; ciec = "ćcy"; cz = "cze"; ciel = "ciele"; -- eunuch, cieć, ?, ?, nauczyciel d = "dzi", dziec = "dźcy"; -- kloszard, jeździec ek = "kowie"; el = "ele"; -- świadek, ? f = "fowie"; -- szeryf g = "dzy"; -- szpieg h = "howie"; -- druh iec = "cy"; ik = "icy"; -- pogrobowiec, czytelnik j = "je"; -- lokaj k = "cy"; -- alkoholik l = "le"; ["ł"] = "łowie"; -- ?, apostoł m = "mi"; -- pielgrzym n = "ni"; ni = "nie"; niec = "ńcy"; -- kompan, leń, jeniec p = "pi"; -- chłop r = "rzy"; rz = "rze"; -- konduktor, murarz s = "si"; si = "sie"; siec = "ścy"; sz = "sze"; -- ordynans, rabuś, ?, jarosz t = "ci"; -- policjant z = "zi"; zi = "zie"; ziec = "źcy"; ["ż"] = "żowie"; -- intruz, kniaź, ?, mąż }		if not nomp_lookup[last] then error("Unsupported word ending: " .. last ..				 "; please define the nominative plural form") end if last == "g" and mw.ustring.match(stem, "lo$") then nomp_form = stem .. "dzy/" .. stem .. "gowie" else nomp_form = stem .. nomp_lookup[last] end end

local declinfo = masc_common("m-pr", stem, last, "a", noms_form,		nomp_form, altgenp, explicit_stem) return handle_overrides(pargs, declinfo,		{ gens = {"accs"}; locs = {"vocs"}; nomp = {"vocp"}; genp = {"accp"} }) end

-- masculine personal nouns with adjectival declension -- e.g. radny, łowczy, salowy patterns["m-pr-adj"] = function (pargs, title) local word = pargs[1] or title if mw.ustring.match(word, "^{{{") then word = "przykładowy" end local decl = m_adj.autoinflect(word) local declinfo = { noms = decl[1]; nomp = decl[4]; gens = decl[6]; genp = decl[8]; dats = decl[9]; datp = decl[10]; accs = decl[6]; accp = decl[8]; inss = decl[12]; insp = decl[13]; locs = decl[12]; locp = decl[8]; vocs = decl[1]; vocp = decl[4]; }	return handle_overrides(pargs, declinfo,		{ noms = {"vocs"}; gens = {"accs"}; nomp = {"vocp"}; genp = {"accp", "locp"} }) end

-- masculine personal nouns that end in -a -- e.g. stażysta, dawca, banita patterns["m-pr-a"] = function (pargs, word) local word_no_a = mw.ustring.match(word, "^(.*)a$") or "" local endings = make_lookup_table("b bi c ch d g j p r st t zd zn ż") local stem, last = split_stem(word_no_a, endings) stem = nonempty(pargs[1]) or stem last = nonempty(pargs[2]) or last

if not endings[last] then -- suppress module error on the template page if not mw.ustring.match(last, "^{{{") then error("Unsupported word ending: " .. last) end end

local gens_form = stem .. last .. "y" if last == "j" then gens_form = stem .. "i" elseif last == "g" then gens_form = stem .. "gi" end local dats_lookup = { b = "bie"; -- Barnaba bi = "bi"; c = "cy"; -- zdrajca ch = "sze"; -- monarcha d = "dzie"; -- nomada (?) g = "dze"; -- sługa j = "i"; -- kaznodzieja p = "pie"; -- satrapa r = "rze"; -- sknera st = "ście"; -- starosta t = "cie"; -- idiota zd = "ździe"; -- gazda zn = "źnie"; -- mężczyzna ["ż"] = "ży"; -- doża }	local dats_form = stem .. (dats_lookup[last] or last)

local nomp_lookup = { b = "bowie"; bi = "biowie"; c = "cy"; ch = "chowie"; d = "dzi"; g = "dzy"; j = "je"; p = "powie"; r = "rzy"; st = "ści"; t = "ci"; zd = "zdowie"; zn = "źni"; ["ż"] = "żowie"; }	local nomp_form = stem .. (nomp_lookup[last] or (last .. "i")) local genp_form = stem .. last .. "ów" if last == "zn" then genp_form = stem .. last end

-- TODO: alternative adjectival declension for -bia local declinfo = { noms = stem .. last .. "a"; nomp = nomp_form; gens = gens_form;           genp = genp_form; dats = dats_form;           datp = stem .. last .. "om"; accs = stem .. last .. "ę"; accp = genp_form; inss = stem .. last .. "ą"; insp = stem .. last .. "ami"; locs = dats_form;           locp = stem .. last .. "ach"; vocs = stem .. last .. "o"; vocp = nomp_form; }	return handle_overrides(pargs, declinfo,		{ dats = {"locs"}; nomp = {"vocp"}; genp = {"accp"} }) end

-- masculine nouns ending in -log, both personal and inanimate -- inanimate form is chosen when the first parameter is empty patterns["log"] = function (pargs, title) local stem = nonempty(pargs[1]) or mw.ustring.match(title, "^(.*)log$") or "" local nomp_form = nonempty(pargs[2]) local inanimate = not nonempty(pargs[1])

if not nomp_form then if inanimate then nomp_form = stem .. "logi" else nomp_form = stem .. "lodzy/" .. stem .. "logowie" end end

local gens_form = stem .. "loga" if inanimate then gens_form = stem .. "logu" end

local accs_form = stem .. "loga" if inanimate then accs_form = stem .. "log" end

local declinfo = { noms = stem .. "log";    nomp = nomp_form; gens = gens_form;        genp = stem .. "logów"; dats = stem .. "logowi"; datp = stem .. "logom"; accs = accs_form;        accp = stem .. "logów"; inss = stem .. "logiem"; insp = stem .. "logami"; locs = stem .. "logu";   locp = stem .. "logach"; vocs = stem .. "logu";   vocp = nomp_form; }	return handle_overrides(pargs, declinfo,		{ locs = {"vocs"}; nomp = {"vocp"}; genp = {"accp"} }) end

-- masculine personal nouns ending with -nin, e.g. Rosjanin, łodzianin -- popular for demonyms patterns["m-pr-nin"] = function (pargs, word) local stem = nonempty(pargs[1]) or mw.ustring.match(word, "^(.*)nin$") local genp_ending = pargs[2] or "n" local declinfo = { noms = stem .. "nin";    nomp = stem .. "nie"; gens = stem .. "nina";   genp = stem .. genp_ending; dats = stem .. "ninowi"; datp = stem .. "nom"; accs = stem .. "nina";   accp = stem .. genp_ending; inss = stem .. "ninem";  insp = stem .. "nami"; locs = stem .. "ninie";  locp = stem .. "nach"; vocs = stem .. "ninie";  vocp = stem .. "nie"; }	return handle_overrides(pargs, declinfo,		{ gens = {"accs"}; locs = {"vocs"}; nomp = {"vocp"}; genp = {"accp"} }) end

-- masculine inanimate adjectives in noun phrases patterns["adj-m-in"] = function (pargs, word) word = nonempty(pargs[1]) or word if mw.ustring.match(word, "^{{{") then word = "przykładowy" end local decl = m_adj.autoinflect(word) local declinfo = { noms = decl[1]; nomp = decl[5]; gens = decl[6]; genp = decl[8]; dats = decl[9]; datp = decl[10]; accs = decl[1]; accp = decl[5]; inss = decl[12]; insp = decl[13]; locs = decl[12]; locp = decl[8]; vocs = decl[1]; vocp = decl[5]; }	return handle_overrides(pargs, declinfo,		{ noms = {"accs", "vocs"}; inss = {"locs"}; nomp = {"accp", "vocp"}; genp = {"locp"} }) end

-- masculine animate adjectives in noun phrases patterns["adj-m-an"] = function (pargs, word) word = nonempty(pargs[1]) or word if mw.ustring.match(word, "^{{{") then word = "przykładowy" end local decl = m_adj.autoinflect(word) local declinfo = { noms = decl[1]; nomp = decl[5]; gens = decl[6]; genp = decl[8]; dats = decl[9]; datp = decl[10]; accs = decl[6]; accp = decl[5]; inss = decl[12]; insp = decl[13]; locs = decl[12]; locp = decl[8]; vocs = decl[1]; vocp = decl[5]; }	return handle_overrides(pargs, declinfo,		{ noms = {"vocs"}; gens = {"accs"}; inss = {"locs"}; nomp = {"accp", "vocp"}; genp = {"locp"} }) end

-- masculine personal adjectives in noun phrases are identical -- to masculine personal adjectival declension

-- - -- -- Feminine declension -- -- -

local fem_endings = make_lookup_table(	"b bi c ch ci cz d dz dzi dż f g h j k l ł m n ni p pi r rz " ..	"s si sł sm sn st sz t w wi z zd zi zł zm zn ż")

-- most feminine nouns ending in -a -- note that some nouns ending in -ia have this declension and others -- follow the "f-ia" pattern - it's impossible to tell from the word alone. patterns["f"] = function (pargs, word) -- to handle pluralia tantum as well local word_no_a = mw.ustring.match(word, "^(.*)[aeiy]$") local pars3 = pargs[1] and pargs[2] and pargs[3] local nopars = not pargs[1] and not pargs[2] and not pargs[3]

-- 2 positional parameters given OR a at the and -> use a-final declension -- 3+ positional parameters given OR consonant at the end -> use f-softcons if nopars then if mw.ustring.match(word, "ia$") then native_ia = mw.ustring.match(word, "[bcdjklłrśtwzź]nia$") native_ia = native_ia or mw.ustring.match(word, "[csz]ia$") if not native_ia then return patterns["f-ia"](pargs, word) end elseif mw.ustring.match(word, "ni$") then return patterns["f-ni"](pargs, word) end end if pars3 or not word_no_a then return patterns["f-softcons"](pargs, word) end

word_no_a = word_no_a or word local stem, last = split_stem(word_no_a, fem_endings) stem = nonempty(pargs[1]) or stem last = nonempty(pargs[2]) or last

if not fem_endings[last] then -- suppress module error on the template page if not mw.ustring.match(last, "^{{{") then error("Unsupported word ending: " .. last) end end

local soft_endings = make_lookup_table("bi ci dzi ni pi si wi zi")

local gens_form = stem .. last .. "y" if soft_endings[last] then gens_form = stem .. last elseif last == "j" then if mw.ustring.match(stem, "[aąeęioóuy]$") then gens_form = stem .. "i" else gens_form = stem .. last .. "i" end elseif (last == "g") or (last == "k") or (last == "l") then gens_form = stem .. last .. "i" end

local dats_lookup = { b = "bie"; bi = "bi"; c = "cy"; ch = "sze"; ci = "ci"; cz = "czy"; d = "dzie"; dz = "dzy"; dzi = "dzi"; ["dż"] = "dży"; f = "fie"; g = "dze"; h = "że"; j = "ji"; k = "ce"; l = "li"; ["ł"] = "le"; m = "mie"; n = "nie"; ni = "ni"; p = "pie"; ["pi"] = "pi"; r = "rze"; rz = "rzy"; s = "sie"; si = "si"; ["sł"] = "śle"; sm = "śmie"; sn = "śnie"; st = "ście"; sz = "szy"; t = "cie"; w = "wie"; wi = "wi"; z = "zie"; zd = "ździe"; zi = "zi"; ["zł"] = "źle"; zm = "zmie"; zn = "źnie"; ["ż"] = "ży"; -- -zm should not be palatalized }	local dats_form = stem .. (dats_lookup[last] or last) if last == "j" and mw.ustring.match(stem, "[aąeęioóuy]$") then dats_form = stem .. "i" end

local nomp_e_ending = make_lookup_table("bi c ci cz dz dzi dż j l ni pi rz si sz wi zi ż") local nomp_form = stem .. last .. "y"

if (last == "g") or (last == "k") then nomp_form = stem .. last .. "i" elseif nomp_e_ending[last] then nomp_form = stem .. last .. "e" end

local genp_form = stem .. last if last == "j" then if mw.ustring.match(stem, "[aąeęioóuy]$") then -- zgraja, breja, żmija, koja, szuja, chryja -> -- zgraj, brej, żmij, koj, szuj, chryj genp_form = stem .. last else -- e.g. gracja, torsja genp_form = stem .. "ji/" .. stem .. "yj" end elseif last == "l" then if mw.ustring.match(stem, "[aąeęioóuy]$") then -- hala, tabela, mila, rola, kula genp_form = stem .. "l" else -- hodowla, grobla, bernikla genp_form = stem .. "li" end elseif last == "k" then local kstem, klast = split_stem(stem, soft_ending_lookup) if kstem then kstem = kstem .. klast else kstem = stem end if mw.ustring.match(stem, "[aąeęioóuy]$") then genp_form = kstem .. "k" else genp_form = kstem .. "ek" end end

local declinfo = { noms = stem .. last .. "a"; nomp = nomp_form; gens = gens_form;           genp = genp_form; dats = dats_form;           datp = stem .. last .. "om"; accs = stem .. last .. "ę"; accp = nomp_form; inss = stem .. last .. "ą"; insp = stem .. last .. "ami"; locs = dats_form;           locp = stem .. last .. "ach"; vocs = stem .. last .. "o"; vocp = nomp_form; }	return handle_overrides(pargs, declinfo,		{ dats = {"locs"}; nomp = {"accp", "vocp"} }) end

-- feminine nouns ending with a consonant, e.g. stal, sól, brew patterns["f-softcons"] = function (pargs, title) local endings = make_lookup_table("c ć cz dz dź ew iew j l ń rz ś sz w z ż ź") local accepted_lasts = make_lookup_table("c ci cz dz dzi j l ni rz si sz wi zi ż") local stem, last = split_stem(title, endings) local explicit_stem = false stem = nonempty(pargs[1]) or stem last = nonempty(pargs[2]) or last if nonempty(pargs[2]) then stem = pargs[1] or "" last = pargs[2] or "" explicit_stem = true end local nomp_ending = pargs[3] -- may be empty local noms_form = nonempty(pargs[4])

-- Narew, brukiew, żagiew, brew - elide -e- or -ie- if last == "iew" or last == "ew" then if not noms_form then noms_form = stem .. last end last = "wi" end

if last == "w" or last == "z" then last = last .. "i" end if not accepted_lasts[last] then -- suppress module error on the template page if not last or not mw.ustring.match(last, "^{{{") then error("Unsupported word ending: " .. (last or "nil")) end end

-- nominative singular if not noms_form or (noms_form == "") then local noms_lookup = { ci = "ć"; dzi = "dź"; ni = "ń"; si = "ś"; wi = "w"; zi = "ź"; } noms_form = stem .. (noms_lookup[last] or last) end -- Stem-final ó becoming o in inflected forms if mw.ustring.match(stem, "ó$") and not explicit_stem then stem = mw.ustring.sub(stem, 1, -2) .. "o" end

-- genitive singular local gens_form = stem .. last local gens_y_ending = make_lookup_table("c cz dz sz rz ż") if last == "j" then gens_form = stem .. "i" elseif last == "l" then gens_form = gens_form .. "i" elseif gens_y_ending[last] then gens_form = gens_form .. "y" end

-- nominative plural if not nomp_ending then if last == "ci" then -- some words have -e, but this is a lot more common nomp_ending = "" else nomp_ending = "e" end end -- this is trivial, but used in 3 places in the table local nomp_form = stem .. last .. nomp_ending;

local declinfo = { noms = noms_form;           nomp = nomp_form; gens = gens_form;           genp = gens_form; dats = gens_form;           datp = stem .. last .. "om"; accs = noms_form;           accp = nomp_form; inss = stem .. last .. "ą"; insp = stem .. last .. "ami"; locs = gens_form;           locp = stem .. last .. "ach"; vocs = gens_form;           vocp = nomp_form; }	return handle_overrides(pargs, declinfo,		{ noms = {"accs"}; gens = {"dats", "locs", "vocs", "genp"}; nomp = {"accp", "vocp"} }) end

-- feminine nouns with adjectival declension patterns["f-adj"] = function (pargs, title) local word = nonempty(pargs[1]) or title if mw.ustring.match(word, "^{{{") then word = "przykładowa" end local decl = m_adj.autoinflect(word) local declinfo = { noms = decl[2];  nomp = decl[5]; gens = decl[7];  genp = decl[8]; dats = decl[7];  datp = decl[10]; accs = decl[11]; accp = decl[5]; inss = decl[11]; insp = decl[13]; locs = decl[7];  locp = decl[8]; vocs = decl[2];  vocp = decl[5]; }	return handle_overrides(pargs, declinfo,		{ gens = {"dats", "locs"}; accs = {"inss"}; nomp = {"accp", "vocp"}; genp = {"locp"} }) -- "vocs" may not always be equal to "noms" (e.g. "teściowa") end

-- feminine nouns ending with -ja, e.g. fuzja, anomizja, animacja -- obsolete - "f" handles them as well patterns["f-ja"] = function (pargs, title) local stem = nonempty(pargs[1]) or mw.ustring.match(title, "^(.*)ja$") local yj = "yj" if pargs[2] and (pargs[2] ~= "") then yj = "ij" end genp_form = stem .. "ji/" .. stem .. yj;

local declinfo = { noms = stem .. "ja"; nomp = stem .. "je"; gens = stem .. "ji"; genp = genp_form; dats = stem .. "ji"; datp = stem .. "jom"; accs = stem .. "ję"; accp = stem .. "je"; inss = stem .. "ją"; insp = stem .. "jami"; locs = stem .. "ji"; locp = stem .. "jach"; vocs = stem .. "jo"; vocp = stem .. "je"; }	return handle_overrides(pargs, declinfo,		{ gens = {"dats", "locs"}; nomp = {"accp", "vocp"} }) end

-- most feminine nouns ending with -ia, e.g. mafia, kopia, balia -- note that some nouns follow the "f" pattern instead, e.g. konopia, głębia patterns["f-ia"] = function (pargs, title) local stem = nonempty(pargs[1]) or mw.ustring.match(title, "^(.*)ia$") local oldgenp = stem .. "ij" if mw.ustring.match(stem, "[dtr]$") then oldgenp = stem .. "yj" end local genp_form = stem .. "ii/" .. oldgenp; local declinfo = { noms = stem .. "ia"; nomp = stem .. "ie"; gens = stem .. "ii"; genp = genp_form; dats = stem .. "ii"; datp = stem .. "iom"; accs = stem .. "ię"; accp = stem .. "ie"; inss = stem .. "ią"; insp = stem .. "iami"; locs = stem .. "ii"; locp = stem .. "iach"; vocs = stem .. "io"; vocp = stem .. "ie"; }	return handle_overrides(pargs, declinfo,		{ gens = {"dats", "locs"}; nomp = {"accp", "vocp"} }) end

-- feminine nouns ending in -ni, e.g. mistrzyni, mędrczyni patterns["f-ni"] = function (pargs, title) local stem = nonempty(pargs[1]) or mw.ustring.match(title, "^(.*)ni$") local declinfo = { noms = stem .. "ni"; nomp = stem .. "nie"; gens = stem .. "ni"; genp = stem .. "ń"; dats = stem .. "ni"; datp = stem .. "niom"; accs = stem .. "nię"; accp = stem .. "nie"; inss = stem .. "nią"; insp = stem .. "niami"; locs = stem .. "ni"; locp = stem .. "niach"; vocs = stem .. "ni"; vocp = stem .. "nie"; }	return handle_overrides(pargs, declinfo,		{ noms = {"gens", "dats", "locs", "vocs"}; nomp = {"accp", "vocp"} }) end

-- feminine adjectives in noun phrases patterns["adj-f"] = function (pargs, title) local word = nonempty(pargs[1]) or title if mw.ustring.match(word, "^{{{") then word = "przykładowa" end local decl = m_adj.autoinflect(word) local declinfo = { noms = decl[2]; nomp = decl[5]; gens = decl[7]; genp = decl[8]; dats = decl[7]; datp = decl[10]; accs = decl[11]; accp = decl[5]; inss = decl[11]; insp = decl[13]; locs = decl[7]; locp = decl[8]; vocs = decl[2]; vocp = decl[5]; }	return handle_overrides(pargs, declinfo,		{ noms = {"vocs"}; gens = {"dats", "locs"}; accs = {"inss"}; nomp = {"accp", "vocp"}; genp = {"locp"} }) end

-- - -- -- Neuter declension - -- -

-- neuter nouns ending in -o, e.g. jajko, jarzmo, gusło, cudo patterns["n"] = function (pargs, word) if pargs[1] and mw.ustring.match(pargs[1], "^{{{") then word = "jajko" end

-- forward to other patterns depending on the last letter if not nonempty(pargs[1]) and not nonempty(pargs[2]) then if mw.ustring.match(word, "e$") then return patterns["n-e"](pargs, word) elseif mw.ustring.match(word, "ę$") then return patterns["n-ę"](pargs, word) elseif mw.ustring.match(word, "um$") then return patterns["n-um"](pargs, word) end end

local word_no_o = mw.ustring.match(word, "^(.*)o$") local endings = make_lookup_table(		"b bn c ch d f g gn j k kn l ł m mn n nd p r rz rzm " ..		"s sk sł sm sn st t tt tw w wn z zd zł zm zn zz") local stem, last if word_no_o then stem, last = split_stem(word_no_o, endings) end stem = nonempty(pargs[1]) or stem last = nonempty(pargs[2]) or last local genp_form = nonempty(pargs[3])

if not endings[last] then -- suppress module error on the template page if not mw.ustring.match(last, "^{{{") then error("Unsupported word ending: " .. last) end end

local inss_form = stem .. last .. "em" if last == "g" or last == "k" then inss_form = stem .. last .. "iem" end

local locs_lookup = { b = "bie"; bn = "bnie"; c = "cu"; ch = "chu"; d = "dzie"; f = "fie"; g = "gu"; gn = "gnie"; j = "ju"; k = "ku"; kn = "knie"; l = "lu"; ["ł"] = "le"; m = "mie"; mn = "mnie"; n = "nie"; nd = "ndzie"; p = "pie"; r = "rze"; rz = "rzu"; rzm = "rzmie"; -- piórze, scherzu, jarzmie s = "sie"; sk = "sku"; ["sł"] = "śle"; sm = "śmie"; sn = "śnie"; st = "ście"; t = "cie"; tt = "tcie"; tw = "twie"; w = "wie"; wn = "wnie"; z = "zie"; zd = "ździe"; ["zł"] = "źle"; zm = "źmie"; zn = "źnie"; zz = "zzu"; }

locs_form = stem .. (locs_lookup[last] or (last .. "u"))

if not genp_form then if last == "sn" or last == "zn" then genp_form = stem .. mw.ustring.sub(last, 1, 1) .. "en" -- e.g. krosno -> krosen elseif mw.ustring.match(last, "^.n$") then genp_form = stem .. mw.ustring.sub(last, 1, 1) .. "ien" -- e.g. bagno -> bagien elseif last == "tw" or last == "sk" or mw.ustring.match(stem, "[aąeęioóuy]$") then genp_form = stem .. last -- e.g. bogactwo -> bogactw, dyktando -> dyktand (nd treated as digraph) else if mw.ustring.match(stem, "[gk]$") then genp_form = stem .. "ie" .. last -- e.g. szkło -> szkieł else genp_form = stem .. "e" .. last -- e.g. jajko -> jajek end end end local declinfo = { noms = stem .. last .. "o"; nomp = stem .. last .. "a"; gens = stem .. last .. "a"; genp = genp_form; dats = stem .. last .. "u"; datp = stem .. last .. "om"; accs = stem .. last .. "o"; accp = stem .. last .. "a"; inss = inss_form;           insp = stem .. last .. "ami"; locs = locs_form;           locp = stem .. last .. "ach"; vocs = stem .. last .. "o"; vocp = stem .. last .. "a"; }	return handle_overrides(pargs, declinfo,		{ noms = {"accs", "vocs"}; gens = {"nomp", "accp", "vocp"}; nomp = {"accp", "vocp"} }) end

-- neuter nouns ending in -e patterns["n-e"] = function (pargs, word) local word_no_e = mw.ustring.match(word, "^(.*)e$") local endings = make_lookup_table("bi c ci cz dz dzi fi j l mi ni pi rz si sz wi zi ż") local stem, last if word_no_e then stem, last = split_stem(word_no_e, endings) end if nonempty(pargs[1]) then stem = pargs[1] last = "" end local genp_lookup = { ci = "ć"; cz = "czy"; ni = "ń"; rz = "rzy"; sz = "szy"; ["ż"] = "ży"; } local genp_form = pargs[3] or (stem .. (genp_lookup[last] or last)) local sl = stem .. last

local declinfo = { noms = sl .. "e";  nomp = sl .. "a"; gens = sl .. "a";  genp = genp_form; dats = sl .. "u";  datp = sl .. "om"; accs = sl .. "e";  accp = sl .. "a"; inss = sl .. "em"; insp = sl .. "ami"; locs = sl .. "u";  locp = sl .. "ach"; vocs = sl .. "e";  vocp = sl .. "a"; }	return handle_overrides(pargs, declinfo,		{ noms = {"accs", "vocs"}; gens = {"nomp", "accp", "vocp"}; dats = {"locs"}; nomp = {"accp", "vocp"} }) end

-- neuter nouns ending in -ę but not in -mię, e.g. kocię, szczenię, dziecię patterns["n-ę"] = function (pargs, title) local mstem = mw.ustring.match(title, "^(.*)mię$") if mstem then pargs[1] = mstem return patterns["n-mię"](pargs, title) end local stem = nonempty(pargs[1]) or mw.ustring.match(title, "^(.*)ę$") local declinfo = { noms = stem .. "ę";    nomp = stem .. "ęta"; gens = stem .. "ęcia"; genp = stem .. "ąt"; dats = stem .. "ęciu"; datp = stem .. "ętom"; accs = stem .. "ę";    accp = stem .. "ęta"; inss = stem .. "ęciem"; insp = stem .. "ętami"; locs = stem .. "ęciu"; locp = stem .. "ętach"; vocs = stem .. "ę";    vocp = stem .. "ęta"; }	return handle_overrides(pargs, declinfo,		{ noms = {"accs", "vocs"}; dats = {"locs"}; nomp = {"accp", "vocp"} }) end

-- neuter nouns ending in -mię, e.g. wymię, znamię, plemię patterns["n-mię"] = function (pargs, title) local stem = nonempty(pargs[1]) or mw.ustring.match(title, "^(.*)mię$") local declinfo = { noms = stem .. "mię";     nomp = stem .. "miona"; gens = stem .. "mienia";  genp = stem .. "mion"; dats = stem .. "mieniu";  datp = stem .. "mionom"; accs = stem .. "mię";     accp = stem .. "miona"; inss = stem .. "mieniem"; insp = stem .. "mionami"; locs = stem .. "mieniu";  locp = stem .. "mionach"; vocs = stem .. "mię";     vocp = stem .. "miona"; }	return handle_overrides(pargs, declinfo,		{ noms = {"accs", "vocs"}; dats = {"locs"}; nomp = {"accp", "vocp"} }) end

-- neuter nouns with adjectival declension, e.g. bykowe patterns["n-adj"] = function (pargs, title) local word = nonempty(pargs[1]) or title if mw.ustring.match(word, "^{{{") then word = "przykładowe" end

local decl = m_adj.autoinflect(word) local declinfo = { noms = decl[3];  nomp = decl[5]; gens = decl[6];  genp = decl[8]; dats = decl[9];  datp = decl[10]; accs = decl[3];  accp = decl[5]; inss = decl[12]; insp = decl[13]; locs = decl[12]; locp = decl[8]; vocs = decl[3];  vocp = decl[5]; }	return handle_overrides(pargs, declinfo,		{ noms = {"accs", "vocs"}; inss = {"locs"}; nomp = {"accp", "vocp"}; genp = {"locp"} }) end

-- neuter nouns ending in -um, e.g. liceum, gimnazjum, kryterium -- mostly nouns borrow from ancient Greek patterns["n-um"] = function (pargs, title) local stem = nonempty(pargs[1]) or mw.ustring.match(title, "^(.*)um$") local declinfo = { noms = stem .. "um"; nomp = stem .. "a"; gens = stem .. "um"; genp = stem .. "ów"; dats = stem .. "um"; datp = stem .. "om"; accs = stem .. "um"; accp = stem .. "a"; inss = stem .. "um"; insp = stem .. "ami"; locs = stem .. "um"; locp = stem .. "ach"; vocs = stem .. "um"; vocp = stem .. "a"; }	return handle_overrides(pargs, declinfo,		{ nomp = {"accp", "vocp"} }) end

-- highly irregular nouns patterns["irreg"] = function (pargs, title) local word = make_lookup_table("brat dziecko ksiądz książę rok") if not word[title] then if not mw.ustring.match(title, "^{{{") then error("Unsupported word") end end -- brat if title == "brat" then noms_form = "brat" gens_form = "brata" dats_form = "bratu" accs_form = "brata" inss_form = "bratem" locs_form = "bracie" vocs_form = "bracie" nomp_form = "bracia" genp_form = "braci" datp_form = "braciom" accp_form = "braci" insp_form = "braćmi" locp_form = "braciach" vocp_form = "bracia" end -- dziecko if title == "dziecko" then noms_form = "dziecko" gens_form = "dziecka" dats_form = "dziecku" accs_form = "dziecko" inss_form = "dzieckiem" locs_form = "dziecku" vocs_form = "dziecko" nomp_form = "dzieci" genp_form = "dzieci" datp_form = "dzieciom" accp_form = "dzieci" insp_form = "dziećmi" locp_form = "dzieciach" vocp_form = "dzieci" end --ksiądz if title == "ksiądz" then noms_form = "ksiądz" gens_form = "księdza" dats_form = "księdzu" accs_form = "księdza" inss_form = "księdzem" locs_form = "księdzu" vocs_form = "księże" nomp_form = "księża" genp_form = "księży" datp_form = "księżom" accp_form = "księży" insp_form = "księżmi" locp_form = "księżach" vocp_form = "księża" end --książę if title == "książę" then noms_form = "książę" gens_form = "księcia" dats_form = "księciu" accs_form = "księcia" inss_form = "księciem" locs_form = "księciu" vocs_form = "książę" nomp_form = "książęta" genp_form = "książąt" datp_form = "książętom" accp_form = "książąt" insp_form = "książętami" locp_form = "książętach" vocp_form = "książęta" end -- rok if title == "rok" then noms_form = "rok" gens_form = "roku" dats_form = "rokowi" accs_form = "rok" inss_form = "rokiem" locs_form = "roku" vocs_form = "roku" nomp_form = "lata" genp_form = "lat" datp_form = "latom" accp_form = "lata" insp_form = "latami" locp_form = "latach" vocp_form = "lata" end local declinfo = { noms = noms_form;           nomp = nomp_form; gens = gens_form;           genp = genp_form; dats = dats_form;           datp = datp_form; accs = accs_form;           accp = accp_form; inss = inss_form;           insp = insp_form; locs = locs_form;           locp = locp_form; vocs = vocs_form;           vocp = vocp_form; }	return handle_overrides(pargs, declinfo, {}) end

-- indeclinable pattern, i.e. same word for all cases patterns["indec"] = function (pargs, title) local word = nonempty(pargs[1]) or title local declinfo = {} for i = 1, 7 do		for _, col in ipairs(noun_cols) do declinfo[cases[i].key .. col.key] = word end end return handle_overrides(pargs, declinfo, {}) end

-- shorthands patterns["nin"] = patterns["m-pr-nin"] patterns["ia"] = patterns["fem-ia"]

-- aliases for adjectives in phrases patterns["adj-m-pr"] = patterns["m-pr-adj"] patterns["adj-n"] = patterns["n-adj"]

-- used for autodetection of adjective genders in phrases local function pattern_gender(pattern) if pattern == "m-in" or pattern == "adj-m-in" then return "m-in" elseif pattern == "m-an" or pattern == "adj-m-an" then return "m-an" elseif string.match(pattern, "^m-pr") or pattern == "adj-m-pr" then return "m-pr" elseif string.match(pattern, "^f") or pattern == "adj-f" then return "f" elseif string.match(pattern, "^n") or pattern == "adj-n" then return "n" end return nil end

-- generate inflected forms given pattern, parameters, and full word -- returned words do not contain links function export.autoinflect(pattern, pargs, word) if not pattern then error("Declension pattern not specified") end if mw.ustring.match(pattern, "^{{{") then pattern = "m-in" end if not patterns[pattern] then error("Invalid declension pattern: " .. pattern) end return patterns[pattern](pargs, pargs["lemma"] or word) end

local function make_substitutable_table(pargs, declinfo, preproc) local tantum = normalize_tantum(pargs) if mw.isSubsting then if tantum == "s" or tantum == "p" then local tname = "sing" if tantum == "p" then tname = "pl" end local rows = {} for i = 1, 7 do local case_key = cases[i].key .. tantum table.insert(rows, ("| %s"):format(case_key, m_links.remove_links(declinfo[case_key]))) end return "{{pl-decl-noun-" .. tname .. "\n" .. table.concat(rows, "\n") .. "\n}}" end

local rows = {} for i = 1, 7 do local case_skey = cases[i].key .. "s" local case_pkey = cases[i].key .. "p" table.insert(rows, ("| %s"):format(case_skey, m_links.remove_links(declinfo[case_skey]))) table.insert(rows, ("| %s"):format(case_pkey, m_links.remove_links(declinfo[case_pkey]))) end return "{{pl-decl-noun\n" .. table.concat(rows, "\n") .. "\n}}" else local cols = noun_cols if tantum == "s" then cols = { noun_cols[1] } elseif tantum == "p" then cols = { noun_cols[2] } end cols = override_col_titles(pargs.heads, cols)

if pargs.nocat then tantum = nil end return make_table(declinfo, cols, preproc, pargs.width, pargs.title, tantum, pargs.nolinks) end end

-- Generate declension table for a specified declension pattern function export.template_decl_pattern(frame) local args = frame.args local pargs = frame:getParent.args local pagetitle = mw.title.getCurrentTitle.fullText

-- support "num" as fallback for "tantum" to match Latin templates local tantum = pargs.tantum or pargs.num if tantum == "sg" then tantum = "s" elseif tantum == "pl" then tantum = "p" end

if get_mode == 'demo' then pargs = { "{{{1}}}", "{{{2}}}", "{{{3}}}", "{{{4}}}", "{{{5}}}" } end

-- if args is empty, use pargs[1] as the pattern name and shift pargs local offset = 0 local pattern = args[1] if not pattern then -- extreme brokenness: table.remove(pargs, 1) has absolutely no effect! -- is this because pargs is somehow immutable? pattern = pargs[1] offset = -1 end

local fixed_pargs = {} for key, parg in pairs(pargs) do		if type(key) == "number" then local i = key + offset if i >= 1 and parg then fixed_pargs[i] = m_links.remove_links(parg) end else fixed_pargs[key] = m_links.remove_links(parg) end end

declinfo = export.autoinflect(pattern, fixed_pargs, pagetitle) return make_substitutable_table(pargs, declinfo, "linkify") end

local function push_down_indices(tbl) local ret = {} for index, decl in ipairs(tbl) do		for k, v in pairs(decl) do			ret[k] = ret[k] or {} table.insert(ret[k], v)		end end return ret end

-- Generate declension table for a noun phrase function export.template_decl_phrase(frame) local args = frame.args local pargs = frame:getParent.args local page_title = mw.title.getCurrentTitle.fullText local lemma = pargs["lemma"] or page_title local words = {}

if get_mode == "demo" then pargs = { "adj", "n", "adj" } lemma = "przykładowe wyrażenie rzeczownikowe" end

-- split title into words for t in mw.ustring.gmatch(lemma, "[^ ]+") do		table.insert(words, t)	end

local word_args = {} for index, word in ipairs(words) do		-- parse arguments to declension patterns word_args[index] = {} local numkey = 1 local arg = pargs[index] or "" for i in mw.ustring.gmatch(arg, "[^!]+") do			local param = mw.text.trim(i) local k, v = mw.ustring.match(param, "^([^:]*):(.*)$") if k and v then word_args[index][k] = v			else word_args[index][numkey] = param numkey = numkey + 1 end end end

local adj_gender = nil for index, word in ipairs(words) do		-- find the first gendered pattern and use it to match adjectives if not adj_gender then adj_gender = pattern_gender(word_args[index][1]) end end

local result = {} local result_raw = {}

for index, word in ipairs(words) do		local word_result = {} local pattern = table.remove(word_args[index], 1) if pattern == "adj" then if adj_gender then pattern = "adj-" .. adj_gender else error("Unable to guess adjective gender") end end if not patterns[pattern] then -- indeclinable pattern = "indec" end local nolinks = word_args[index].nolinks or pargs.linkfull result_raw[index] = export.autoinflect(pattern, word_args[index], word) result[index] = linkify_info(result_raw[index], altsep, nolinks) end

-- rearrange the table so that word indices are at the lower level result_raw = push_down_indices(result_raw) result = push_down_indices(result)

-- make table entries for case, v in pairs(result_raw) do		local phrase = table.concat(v, " ") if phrase == page_title then -- bold self-link if the phrase is equal to the page title result[case] = "" .. page_title .. "" else result[case] = table.concat(result[case], " ") if pargs.linkfull then -- do not split if linking full phrase declensions result[case] = export.make_links(result[case], nil) end end end

-- width determination if not pargs.width then local result_concat = {} for case, v in pairs(result_raw) do			result_concat[case] = table.concat(v, " ") end pargs.width = guess_width(result_concat, noun_cols) end

-- disable linkifying, since we already made per-word links return make_substitutable_table(pargs, result, nil) end

return export