local export = {}
local vowels = "аӓеёиоуыҍэӭюя"
local consonants = "бвджзгһйҋклӆмӎнӊӈпрҏстфхцчшщ"
local macron = mw.ustring.char(0x0304)
-- also includes digraphs for monophthongs, despite its name
local diphthongs = {
"оа", "ёа",
"оа̄", "ёа̄",
"уэ", "уа",
}
-- e.g. ьэ => е
local fusion = {
["ьэ"] = "е", ["ьа"] = "я",
["ҍэ"] = "ӭ", ["ҍа"] = "ӓ",
["йэ"] = "е", ["йа"] = "я",
["чэ"] = "че",
-- remove redundant palatal markers
["ьи"] = "и", ["ҍи"] = "и",
-- ["ье"] = "е",
["ҍӭ"] = "ӭ",
-- ["ья"] = "я",
["ҍӓ"] = "ӓ",
["ьё"] = "ё", ["ҍё"] = "ё",
["ью"] = "ю", ["ҍю"] = "ю",
}
-- e.g. a word ending is е is actually ь + э
local fission = {
["е"] = { "ь", "э" },
["я"] = { "ь", "а" },
["ӭ"] = { "ҍ", "э" },
["ӓ"] = { "ҍ", "а" },
}
-- precomposed Unicode macrons (i.e. ones that don't require a separate combining macron codepoint)
local macrons = {
["ӣ"] = "и",
["ӯ"] = "у",
}
local macroned = ""
local macroned_reverse = ""
local macrons_with_macron = {}
local macrons_reversed = {}
for k, v in pairs(macrons) do
macroned = macroned .. k
macroned_reverse = macroned_reverse .. v
macrons_reversed[v .. macron] = k
macrons_with_macron[k] = v .. macron
end
local diphthongs_by_final = {}
for _, v in ipairs(diphthongs) do
local a, b = mw.ustring.sub(v, 1, 1), mw.ustring.sub(v, 2)
diphthongs_by_final[b] = (diphthongs_by_final[b] or "") .. a
end
local function extract(word, ending)
if mw.ustring.match(word, ending .. "$") then
return mw.ustring.match(word, "(.-)" .. ending .. "$")
end
error("Unexpected ending for this inflection type! Wrong type?")
end
local function extract_final_vowel(word)
local stem, vowel, macron = extract(word, "([" .. vowels .. "])(" .. macron .. "?)")
if diphthongs_by_final[vowel .. macron] and mw.ustring.match(stem, "[" .. diphthongs_by_final[vowel .. macron] .. "]$") then
vowel = mw.ustring.sub(stem, -1, -1) .. vowel .. macron
stem = mw.ustring.sub(stem, 1, -2)
end
if fission[vowel] then
local stem_extra
stem_extra, vowel = unpack(fission[vowel])
stem = stem .. stem_extra
end
if vowel == "оа̄" then vowel = "оа" end
if vowel == "ёа̄" then vowel = "ёа" end
return stem, vowel .. macron
end
local function decompose_macrons(word)
return mw.ustring.gsub(word, "[" .. macroned .. "]", macrons_with_macron)
end
local function recompose_macrons(word)
return mw.ustring.gsub(word, "[" .. macroned_reverse .. "]" .. macron, macrons_reversed)
end
local function append_hard_sign(t, x)
-- hard sign
if mw.ustring.match(x, "^[ея]") and mw.ustring.match(t, "[" .. consonants .. "]$") then
return t .. "ъ" .. x
else
return t .. x
end
end
local function fusion_special_case(a, b)
if mw.ustring.match(a, "нь$") then
if mw.ustring.match(b, "^оа") then
return mw.ustring.sub(a, 1, -2) .. "ё" .. mw.ustring.sub(b, 2)
elseif mw.ustring.match(b, "^уэ") then
return mw.ustring.sub(a, 1, -2) .. "ю" .. mw.ustring.sub(b, 2)
end
end
end
local function join(...)
local t = {}
for _, s in ipairs(arg) do
if type(s) == "table" then
for _, v in ipairs(s) do
table.insert(t, v)
end
else
table.insert(t, s)
end
end
return t
end
local function append(t, x)
if not x then return t end
if type(t) == "string" then
return t .. x
end
local r = {}
for _, v in ipairs(t) do
table.insert(r, v .. x)
end
return r
end
local function append_smart(t, x)
if not x then return t end
if type(t) == "string" then
-- special case
local spec = fusion_special_case(t, x)
if spec then return spec end
-- two letters
local key = mw.ustring.sub(t, -1, -1) .. mw.ustring.sub(x, 1, 1)
if fusion[key] then
return append_hard_sign(mw.ustring.sub(t, 1, -2), fusion[key] .. mw.ustring.sub(x, 2))
end
return append_hard_sign(t, x)
end
local r = {}
for _, v in ipairs(t) do
table.insert(r, append_smart(v, x))
end
return r
end
export.p=append_smart
local function strip_final_vowel(t)
if type(t) == "string" then
return mw.ustring.gsub(t, "[" .. vowels .. "]+$", "")
end
local r = {}
for _, v in ipairs(t) do
table.insert(r, strip_final_vowel(v))
end
return r
end
local function make_gradation(s, w)
if s == w then
return "no gradation"
else
return s .. "-" .. w .. " gradation"
end
end
local function process(data, result, vh)
local vh = data.vh
-- genitive singular/plural stem
local gs = result.stem_gs
local gp = result.stem_gp
-- illative singular stem
local is = result.stem_is
-- locative singular stem
local ls = result.stem_ls or gs
-- comitative singular stem
local cs = result.stem_cs or gp
-- essive stem
local es = result.stem_es or data.title
-- abessive singular stem
local as = result.stem_as or gs
-- partitive singular stem
local pt = result.stem_as or gs
if not data.no_singular then
result["nom_sg"] = { data.title }
result["gen_sg"] = gs
result["ill_sg"] = is
result["loc_sg"] = append_smart(ls, "сьт")
result["com_sg"] = append_smart(cs, "нҍ")
result["abe_sg"] = result.strong_abessive and append_smart(strip_final_vowel(es), "аһта") or append_smart(as, "ха")
result["ess"] = append_smart(es, "нҍ")
result["prt"] = result.strong_partitive and append_smart(is, "дтӭ") or es
end
if not data.no_plural then
result["nom_pl"] = gs
result["acc_pl"] = append_smart(gp, "тҍ")
result["gen_pl"] = gp
result["ill_pl"] = append_smart(gp, "тҍ")
result["loc_pl"] = append_smart(gp, "нҍ")
result["com_pl"] = append_smart(gp, "гуэйм")
result["abe_pl"] = append_smart(gp, "ха")
end
for k, v in pairs(result) do
if type(v) == "string" then
result[k] = recompose_macrons(v)
elseif type(v) == "table" then
for i = 1,#v do
v[i] = recompose_macrons(v[i])
end
end
end
return result
end
-- inflection classes begin
local inflections = {}
inflections["I"] = function (data)
local vowel_com = data.args[1] or error("must specify plural/comitative vowel")
local vowel_ill = data.args[2] or error("must specify illative vowel")
local strong = data.args[3] or error("must specify strong grade")
local weak = data.args[4] or error("must specify weak grade")
local ending_ill = data.args[5] or error("must specify illative ending")
local result = { typeno = (strong == weak and "II" or "I") }
local word = decompose_macrons(data.title)
local stem, vowel_nom
if data.no_singular then
stem = extract(word, weak)
else
stem = extract(word, strong)
end
stem, vowel_nom = extract_final_vowel(stem)
vowel_com = decompose_macrons(vowel_com)
vowel_ill = decompose_macrons(vowel_ill)
result.stem_gs = append_smart(stem, vowel_nom .. weak)
result.stem_ls = append_smart(append_smart(stem, vowel_nom .. weak), "э")
result.stem_es = append_smart(append_smart(stem, vowel_nom .. strong), "э")
result.stem_is = append_smart(stem, vowel_ill .. ending_ill)
result.stem_gp = append_smart(append_smart(stem, vowel_com .. weak), "э")
result.grade = make_gradation(strong, weak)
return process(data, result)
end
inflections["II"] = inflections["I"]
inflections["III"] = function (data)
local vowel_obl = data.args[1] or error("must specify oblique vowel")
local strong = data.args[2] or error("must specify strong grade")
local extended = data.args[3] or error("must specify extended grade")
local iv = mw.ustring.match(extended, "с$")
local result = { typeno = (iv and "IV" or "III") }
local word = decompose_macrons(data.title)
local stem = word
local weak
if iv then stem = extract(stem, "с") end
stem, weak = extract(stem, "([^" .. vowels .. macron .. "]+)")
local final_v
stem, final_v = extract_final_vowel(stem)
local stem_w = stem .. vowel_obl .. weak
local stem_s = stem .. vowel_obl .. strong
local stem_e = stem .. vowel_obl .. strong .. extended
if mw.ustring.match(extended, "^[" .. vowels .. "]") then
stem_s = stem .. vowel_obl .. strong .. mw.ustring.gsub(extended, "^[" .. vowels .. "]", "")
result.stem_gs = stem_e
result.stem_is = append_smart(stem_s, "э")
result.stem_es = append_smart(stem_s, "э")
result.stem_as = result.stem_es
result.stem_gp = result.stem_es
result.stem_ls = result.stem_es
else
if extended == "к" then
if mw.ustring.match(strong, "[птк][ҍь]?$") then
stem_e = { stem .. vowel_obl .. strong .. "к", stem .. vowel_obl .. strong .. "х" }
elseif mw.ustring.match(strong, "[нмӈлрй][ҍь]?$") then
stem_e = stem .. vowel_obl .. strong .. "г"
end
elseif extended ~= "й" then
error("unsupported grade extension for type III")
end
result.stem_gs = append_smart(stem_s, "э")
result.stem_is = append_smart(stem_e, "э")
result.stem_es = result.stem_gs
result.stem_as = result.stem_gs
result.stem_gp = result.stem_gs
result.stem_ls = result.stem_gs
end
result.strong_partitive = true
result.strong_abessive = true
result.grade = make_gradation(strong, weak)
return process(data, result)
end
inflections["IV"] = inflections["III"]
-- inflection classes end
local infl_table = [=[{| class="inflection-table vsSwitcher" data-toggle-category="inflection" style="border:solid 1px #CCCCFF; text-align: left;" cellspacing="1" cellpadding="2"
|- style="background: #E2F6E2; text-align: left;"
! class="vsToggleElement" colspan="3" | Declension of {{{title}}} (<span style="font-size:90%">{{{type}}}</span>)
|- class="vsShow" style="background: #F2F2FF;"
! style="width: 10em; background: #E2F6E2;" | Nominative
| style="width: 15em;" colspan="2" | {{{nom_sg}}}
|- class="vsShow" style="background: #F2F2FF;"
! style="background: #E2F6E2;" | Genitive
| colspan="2" | {{{gen_sg}}}
|- class="vsHide"
! style="width: 10em; background:#c0e4c0" |
! style="width: 15em; background:#c0e4c0" | singular
! style="width: 15em; background:#c0e4c0" | plural
|- class="vsHide" style="background: #F2F2FF;"
! style="background: #E2F6E2;" | Nominative
| {{{nom_sg}}}
| {{{nom_pl}}}
|- class="vsHide" style="background: #F2F2FF;"
! style="background: #E2F6E2;" | Accusative
| {{{gen_sg}}}
| {{{acc_pl}}}
|- class="vsHide" style="background: #F2F2FF;"
! style="background: #E2F6E2;" | Genitive
| {{{gen_sg}}}
| {{{gen_pl}}}
|- class="vsHide" style="background: #F2F2FF;"
! style="background: #E2F6E2;" | Dative-Illative
| {{{ill_sg}}}
| {{{ill_pl}}}
|- class="vsHide" style="background: #F2F2FF;"
! style="background: #E2F6E2;" | Locative
| {{{loc_sg}}}
| {{{loc_pl}}}
|- class="vsHide" style="background: #F2F2FF;"
! style="background: #E2F6E2;" | Comitative
| {{{com_sg}}}
| {{{com_pl}}}
|- class="vsHide" style="background: #F2F2FF;"
! style="background: #E2F6E2;" | Abessive
| {{{abe_sg}}}
| {{{abe_pl}}}
|- class="vsHide" style="background: #F2F2FF;"
! style="background: #E2F6E2;" | Essive
| colspan="2" | {{{ess}}}
|- class="vsHide" style="background: #F2F2FF;"
! style="background: #E2F6E2;" | Partitive
| colspan="2" | {{{prt}}}
|}]=]
local function link(text)
return require("Module:links").full_link{ term = text, lang = require("Module:languages").getByCode("sjd") }
end
local function mention(text)
return require("Module:links").full_link({ term = text, lang = require("Module:languages").getByCode("sjd") }, "term")
end
function export.show(frame)
local infl_type = frame.args[1] or error("inflection class not specified")
local infl = inflections[infl_type] or error("unsupported inflection type")
local args = frame:getParent().args
local title = args["title"] or mw.title.getCurrentTitle().text
local data = { title = title, headword = headword, args = args }
if args["n"] then
if args["n"] == "s" or args["n"] == "sg" then
data.no_plural = true
elseif args["n"] == "p" or args["n"] == "pl" then
data.no_singular = true
end
end
local forms = infl(data)
local function repl(form)
if form == "title" then
return "'''" .. title .. "'''"
elseif form == "type" then
if forms.irregular then
return "irregular"
end
local s = "type " .. (forms.typeno or infl_type) .. " noun"
if forms.grade then
s = s .. ", " .. forms.grade
else
s = s .. ", " .. make_gradation(nil, nil)
end
return s
else
local value = forms[form]
if not value then
return "—"
elseif type(value) == "table" then
local result = {}
for _, f in ipairs(value) do
table.insert(result, link(f))
end
return table.concat(result, ", ")
else
return link(value)
end
end
end
local result = mw.ustring.gsub(infl_table, "{{{([a-z0-9_:]+)}}}", repl)
result = mw.ustring.gsub(result, "{{m|sjd|([^}]-)}}", mention)
return result
end
return export