Module:sjd-decl

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" ! class="vsToggleElement" colspan="3" | Declension of ! style="width: 10em; background: #E2F6E2;" | Nominative ! style="background: #E2F6E2;" | Genitive ! style="width: 10em; background:#c0e4c0" | ! style="width: 15em; background:#c0e4c0" | singular ! style="width: 15em; background:#c0e4c0" | plural ! style="background: #E2F6E2;" | Nominative ! style="background: #E2F6E2;" | Accusative ! style="background: #E2F6E2;" | Genitive ! style="background: #E2F6E2;" | Dative-Illative ! style="background: #E2F6E2;" | Locative ! style="background: #E2F6E2;" | Comitative ! style="background: #E2F6E2;" | Abessive ! style="background: #E2F6E2;" | Essive ! style="background: #E2F6E2;" | Partitive
 * - style="background: #E2F6E2; text-align: left;"
 * - class="vsShow" style="background: #F2F2FF;"
 * style="width: 15em;" colspan="2" |
 * - class="vsShow" style="background: #F2F2FF;"
 * colspan="2" |
 * - class="vsHide"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * - class="vsHide" style="background: #F2F2FF;"
 * colspan="2" |
 * - class="vsHide" style="background: #F2F2FF;"
 * colspan="2" |
 * }]=]

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 "&mdash;" 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, "", repl) result = mw.ustring.gsub(result, "{{m|sjd|([^}]-)}}", mention) return result end

return export