Module:pl-noun
Jump to navigation
Jump to search
- The following documentation is located at Module:pl-noun/documentation. [edit] Categories were auto-generated by Module:module categorization. [edit]
- Useful links: subpage list • links • transclusions • testcases • sandbox
This module implements {{pl-decl-noun}}
, {{pl-decl-noun-sing}}
, {{pl-decl-noun-pl}}
and {{pl-decl-noun-dual}}
.
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({"vr-p"}, lang) },
{ key = "po"; title = m_g.format_list({"nv-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#Polish|%s]]"):format(text, text)
else
items = {}
for word in mw.ustring.gmatch(text, "[^" .. splitchars .. "]+") do
add_archaic = ""
add_deprecative = ""
if word:sub(-string.len(" (archaic)")) == " (archaic)" then
word = (mw.text.split(word, "% %(archaic%)"))[1]
add_archaic = " (archaic)"
end
if word:sub(-string.len(" (deprecative)")) == " (deprecative)" then
word = (mw.text.split(word, "% %(deprecative%)"))[1]
add_deprecative = " (deprecative)"
end
if word == title then
table.insert(items, ("[[%s]]"):format(word) .. add_archaic .. add_deprecative)
else
table.insert(items, ("[[%s#Polish|%s]]"):format(word, word) .. add_archaic .. add_deprecative)
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 <span class="Latn mention" lang="pl" xml:lang="pl">%s</span>'):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, '|- class="rowgroup"\n! style="background:var(--wikt-palette-lightblue, #d9ebff); width: 8em;" |\n')
for i, col in ipairs(cols) do
table.insert(result, ('! style="background:var(--wikt-palette-lightblue, #d9ebff);" 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" style="background:var(--wikt-palette-lighterblue, #ebf4ff);" 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
local link = m_links.full_link({lang = lang, term = item, accel = {form = ("%s|%s"):format(case.key, col.key)}})
table.insert(result, ('| %s\n'):format(link))
end
end
end
local outtext = ([=[<div class="NavFrame" data-toggle-category="declension" style="display: block; max-width: %uem;">
<div class="NavHead" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" >%s</div>
<div class="NavContent">
{| style="text-align:center; width: 100%%; margin: 0;" class="inflection-table inflection"
]=]):format(emwidth, title) .. table.concat(result, "") .. "|}</div></div>"
if tantum == "s" then
outtext = outtext .. "[[Category:Polish singularia tantum]]"
elseif tantum == "p" then
outtext = outtext .. "[[Category:Polish pluralia tantum]]"
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] = '{{{' .. case_key .. '| {{{' .. argn .. '}}} }}}'
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
local deprecate_nomp_form = ""
if pattern == "m-pr" 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
deprecate_nomp_form = stem .. last .. "i"
elseif nomp_e_ending[last] then
deprecate_nomp_form = stem ..last .. "e"
else
deprecate_nomp_form = stem .. last .. "y"
end
end
-- accusative plural - same as genitive or nominative depending on animacy
local accp_form = (pattern == "m-pr") and genp_form or nomp_form
local full_nomp_form = ((pattern == "m-pr") and deprecate_nomp_form ~= nomp_form and nomp_form .. "/" .. deprecate_nomp_form .. " (deprecative)") or nomp_form
return {
noms = noms_form; nomp = full_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-anml"] = 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-anml", 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
local deprecate_nomp_form = ""
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
deprecate_nomp_form = stem .. last .. "i"
elseif nomp_e_ending[last] then
deprecate_nomp_form = stem ..last .. "e"
else
deprecate_nomp_form = stem .. last .. "y"
end
local full_nomp_form = (deprecate_nomp_form ~= nomp_form and nomp_form .. "/" .. deprecate_nomp_form .. " (deprecative)") or nomp_form
-- TODO: alternative adjectival declension for -bia
local declinfo = {
noms = stem .. last .. "a"; nomp = full_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-anml"] = 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" .. " (archaic)";
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 .. " (archaic)";
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 .. " (archaic)";
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" or pattern == "m-anml" or pattern == "adj-m-anml" then
return "m-anml"
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 --> %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 --> %s"):format(case_skey, m_links.remove_links(declinfo[case_skey])))
table.insert(rows, ("| <!-- %s --> %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 pattern or pattern == "-" then
-- indeclinable
pattern = "indec"
elseif not patterns[pattern] then
error("Unrecognized pattern '" .. pattern .. "'")
end
result_raw[index] = export.autoinflect(pattern, word_args[index], word)
result[index] = linkify_info(result_raw[index], altsep, true)
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], " ")
-- do not split if linking full phrase declensions
result[case] = export.make_links(result[case], nil)
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