Module:amf-nominal

local export = {}

local m_para = require("Module:parameters") local amf = require("Module:languages").getByCode("amf") local m_link = require("Module:links") local m_util = require("Module:amf-utilities")

local DATA = mw.loadData("Module:headword/data") local NAMESPACE = DATA.page.namespace local PAGENAME = DATA.pagename

export.show_table = require("Module:amf-nominal/table")

-- useful regexes and replacement tables local r = {} r.C = "[bBcCdDgGhjklmnNpqrsStwxyzQ]" r.V = "[aeiouEO]" r.add_n = { [""] = "n", b = "mm", -- náabi > námmo B = "mB", -- atáɓ > atámɓa D = "nD", -- tuɗí > tunɗó j = "N", -- cʼagáj > cʼagáɲo k = "ng", -- gerák > gerángo l = "ll", -- afála > afállo m = "mm", -- qáami > qámmo n = "nn", -- ooní > onnó p = "mm", -- galáp > galámmo q = "nq", -- tubáqe > tubánqo r = "rr", -- kurí > kurró S = "N", -- gaʔásh > gaʔáɲo t = "nn", -- qootí > qonnó z = "nn", -- maz > mánno -- missing: cCdgGhNswxyQ } r.add_t = { [""] = "t", b = "tt", -- zóbo > zɔttâ l = "lt", -- ukulí > ukultâ r = "rt", -- góro > gortóno S = "St", -- gaʔásh > gaʔashtóno y = "it", -- gáya > gaitâ }

local genders = { "", "m", "f", "f2", "pl" } local cases = { "nom", "obl", "acc", "gen", "dat", "aff", "ins", "loc", "ine", "ade", "all1", "all2", "abl", "com" } local function combine(g,c) return g .. (g ~= "" and "_" or "") .. c end

local case_suffix = { acc = "ɗan", gen = {"sa", "isa"}, dat = "na",			-- qánte, nánte aff = "kal",		-- ~xal ins = "ka",			-- ~xa loc = "te",			-- te	ine = {"r", "ir"},	-- "ir" is inferred ade = "bar", all1 = "dar", all2 = "shet",		-- shette also abl = {"rra", "irra"},	-- "irra" is inferred com = "be",			-- bet, bette also }

-- see Module:amf-nominal/testcases -- only accepts vowel stem function export.make_masculine(syl) syl = mw.clone(syl) local n = #syl syl.accent = n -- move accent to last syllable syl.falling = true -- make the accent falling if not syl[n]:match("^"..r.C.."[aeio]$") then error("Word must end in -a, -e, -i, or -o.") end syl[n] = syl[n]:gsub("[aeio]$",{e="E",i="E",o="O"}) -- P5	-- regressive vowel harmony; blocked by "i" (MP5) for i=n-1,1,-1 do		if syl[i]:match("i") then break end syl[i] = syl[i]:gsub("[eo]",{e="E",o="O"}) end return syl end

-- clip the last vowel (tesíɓe > *tesíɓ) -- keeps the accent position even if it is at the end function export.truncate_vowel(syl) syl = mw.clone(syl) local n = #syl local cons,vow = syl[n]:match("^("..r.C..")([aeio])$") if cons then syl[n] = nil syl[n-1] = syl[n-1] .. cons end return syl end

-- see Module:amf-nominal/testcases -- add a syllable (CV) to a consonant stem, and apply the phonetic rules -- e.g. panáq + no > panánqo -- e.g. zób + ta > zótta -- shorten the (new) penultimate syllable (e.g. yíir + no > yírro) function export.attach_CV(syl,C,V) syl = mw.clone(syl) local n = #syl local onst,nucl,coda = syl[n]:match("("..r.C.."?)("..r.V.."+)("..r.C.."?)$") if nucl:sub(1,1) == nucl:sub(2,2) and coda ~= "" then nucl = nucl:sub(1,1) end local assimilated = r["add_"..C][coda] or error("Unrecognised pattern: " .. m_util.combine(syl)) syl[n] = onst..nucl..(assimilated:sub(-2,-2)) syl[n+1] = (assimilated:sub(-1,-1))..V	return syl end

function export.make_feminine(syl) return export.attach_CV(syl,"n","o") end

function export.make_f2(data) if data.f2_nom then return nil end data.f2_nom = export.attach_CV(export.truncate_vowel(data.nom),"t","o") local n = #data.f2_nom data.f2_nom.accent = n	data.f2_obl = mw.clone(data.f2_nom) data.f2_nom[n+1] = "no" data.f2_obl[n] = data.f2_obl[n].."n" end

-- generates pl_nom from f_nom local function make_pl(data) data.pl_nom = mw.clone(data.f_nom) local n = #data.pl_nom data.pl_nom[n] = data.pl_nom[n]:gsub("o$","a",1) end

export.inflect = {}

export.inflect["1"] = function(data) data.m_nom = export.make_masculine(data.nom) data.f_nom = "no" data.pl_nom = "na" data.f_obl = "n" end

export.inflect["2"] = function(data) data.m_nom = export.make_masculine(data.nom) data.f_nom = export.make_feminine(export.truncate_vowel(data.nom)) make_pl(data) data.f_obl = "n" end

export.inflect["3"] = function(data) local n = #data.nom data.m_nom = mw.clone(data.nom) local rest,coda = data.nom[n]:match("^(.+)("..r.C..")$") data.m_nom[n] = rest data.m_nom[n+1] = coda .. "a" data.m_nom = export.make_masculine(data.m_nom) data.f_nom = export.make_feminine(data.nom) make_pl(data) data.f_obl = "in" end

-- vowel lowering observed in two out of two samples export.inflect["4i"] = function(data) data.m_nom = mw.clone(data.nom) data.m_nom[2] = "ta" data.m_nom = export.make_masculine(data.m_nom) data.f_nom = "no" -- vowel lowering data.pl_nom = mw.clone(data.nom) data.pl_nom[1] = data.pl_nom[1]:gsub("([eo])%1",{ee="EE",oo="OO"}) data.pl_nom[2] = "na" data.f_obl = "n" end

local function inflect_4(data) local n = #data.nom if data.nom[n]:match("[aeiouEO][aeiouEO]") then data.truncated = mw.clone(data.nom) data.m_nom = mw.clone(data.nom) data.m_nom[n+1] = "ta" else data.truncated = export.truncate_vowel(data.nom) data.m_nom = export.attach_CV(data.truncated,"t","a") end data.m_nom = export.make_masculine(data.m_nom) -- ukultâ data.fem2 = true data.f_obl = "n" end

export.inflect["4a"] = function(data) inflect_4(data) data.f_nom = "no" data.pl_nom = "na" end

export.inflect["4a/"] = function(data) export.inflect["4a"](data) data.m_nom = {data.m_nom, export.make_masculine(data.nom)} end

export.inflect["4b"] = function(data) inflect_4(data) data.f_nom = export.attach_CV(data.truncated,"n","o") make_pl(data) end

export.inflect["4b/"] = function(data) export.inflect["4b"](data) data.m_nom = {data.m_nom, export.make_masculine(data.nom)} end

local function inflect_5(data) local n = #data.nom data.f_nom = mw.clone(data.nom) data.f_nom[n] = "no" make_pl(data) -- for f_obl, the pattern seems to be that if final syllable of f_nom -- is accented, then the (originally) penultimate syllable breaks if	-- it's a diphthong data.f_obl = mw.clone(data.f_nom) if data.f_obl.accent == n then local onset,nucl = data.f_obl[n-1]:match("^("..r.C.."?)("..r.V.."+)$") if #nucl == 2 and nucl:sub(1,1) ~= nucl:sub(2,2) then data.f_obl[n-1] = onset..nucl:sub(1,1) data.f_obl[n] = nucl:sub(2,2).."n" else data.f_obl[n-1] = onset..nucl.."n" data.f_obl[n] = nil data.f_obl.accent = (n>2) and (n-1) or 0 end else data.f_obl[n-1] = data.f_obl[n-1].."n" data.f_obl[n] = nil end end

export.inflect["5a"] = function(data) inflect_5(data) data.m_nom = mw.clone(data.nom) local n = #data.nom data.m_nom[n] = data.m_nom[n]:gsub("i$","a") data.m_nom = export.make_masculine(data.m_nom) end

export.inflect["5b"] = function(data) inflect_5(data) data.m_nom = export.make_masculine(data.nom) end

export.inflect["6"] = function(data) data.nom = data.pagename end

-- m_obl does not exist, but the circumflex changes to acute in other cases -- e.g. hattâ = tree:M; hattá-sa = tree:M-gen function export.make_m_obl(data) if data.m_obl then return nil end data.m_obl = mw.clone(data.m_nom) if type(data.m_obl[1]) == "string" then data.m_obl.falling = false else for i=1,#data.m_obl do			data.m_obl[i].falling = false end end end

function export.combine_nom_obl(data) for _,g in ipairs(genders) do		for _,c in ipairs({"nom","obl"}) do			local gc = combine(g,c) local curr = data[gc] if curr then if data.pattern ~= "6" then if type(curr) == "string" then -- a simple suffix if data.pattern:sub(1,1) ~= "5" then data[gc] = data.pagename .. curr end elseif type(curr[1]) == "string" then -- one form data[gc] = m_util.combine(curr) else for i,syl in ipairs(curr) do							curr[i] = m_util.combine(syl) end data[gc] = table.concat(curr, ", ") end else if curr:match(",") then data[gc] = mw.text.split(curr,",",true) end end end end end end

function export.make_cases(data) -- only f_obl and f2_obl exist but we make the other obl to make the rest of the code easier data.obl = mw.clone(data.nom) data.pl_obl = mw.clone(data.pl_nom) local function attach_suffix(original,suffix) if suffix:sub(1,1) == "b" then original = original:gsub("n$","m") end return original .. suffix end -- make other cases local function make_one_case(g,c,suffix) local source = data[combine(g,"obl")] if not source then return nil elseif type(source) == "string" then data[combine(g,c)] = attach_suffix(source,suffix) else data[combine(g,c)] = {} for i=1,#source do				data[combine(g,c)][i] = attach_suffix(source[i],suffix) end end end for i=3,#cases do		local case = cases[i] local v_suffix, c_suffix if type(case_suffix[case]) == "string" then v_suffix = case_suffix[case] c_suffix = case_suffix[case] else v_suffix = case_suffix[case][1] c_suffix = case_suffix[case][2] end for _,g in ipairs(genders) do			local suffix = (data.pattern == "3" and g == "" and c_suffix or v_suffix) make_one_case(g,case,suffix) end end end

function export.delete_forms(data) if data.modifier == "m" then data.extra = "masculine only" for _,c in ipairs(cases) do			data["f_"..c] = "&mdash;" end elseif data.modifier == "f" then data.extra = "feminine only" for _,c in ipairs(cases) do			data["m_"..c] = "&mdash;" end elseif data.modifier == "sg" then data.extra = "singular only" data.category = "" for _,c in ipairs(cases) do			data["pl_"..c] = "&mdash;" end end for _,g in ipairs(genders) do		for _,c in ipairs(cases) do data[combine(g,c)] = data[combine(g,c)] or "?" end end end

local function determine_pattern(data) local new_data = mw.loadData("Module:amf-nominal/data")[data.pagename] if new_data then for key,val in pairs(new_data) do			data[key] = val end return new_data.pattern end local syl = data.nom if syl[#syl]:sub(-1,-1):match(r.C) then return "3" elseif #syl == 1 and syl[1]:match("^"..r.C.."?"..r.V.."+$") then return "4i" end error("Please specify the declension type.") end

local pattern_display = { ["1"]	= "1",	["2"]	= "2",	["3"]	= "3",	["4i"]	= "4 – inanimate", ["4a"]	= "4 – animate", ["4a/"]	= "4 – animate", ["4b"]	= "4 – animate", ["4b/"]	= "4 – animate", ["5a"]	= "5", ["5b"]	= "5", ["6"]	= "6", }

local auto_patterns = { ["3"] = true, ["4i"] = true, ["5a"] = true, ["5b"] = true, ["6"] = true }

function export.show(frame) local args = m_para.process(frame:getParent.args,{		[1] = {},		[2] = {},		pagename = (NAMESPACE == "Template" or NAMESPACE == "User") and {} or nil,	}) local pagename = args.pagename or PAGENAME if pagename == "hámar" then pagename = "hámari" end local data = {} data.pagename = pagename data.modifier = args[2] if args[2] == "f2" then data.fem2 = true end data.nom = m_util.syllabify(pagename) -- store the base form in data.nom if auto_patterns[args[1]] then error("Declension " .. args[1] .. " does not need to be specified.") end local pattern = args[1] or determine_pattern(data) data.pattern = pattern export.inflect[pattern](data) if data.fem2 then export.make_f2(data) end export.make_m_obl(data) export.combine_nom_obl(data) export.make_cases(data) if pagename == "hámari" then pagename = "hámar" data.nom = "hámar" end for _,g in ipairs(genders) do		for _,c in ipairs(cases) do			local gc = combine(g,c) local val = data[gc] if val and type(val) == "table" then data[gc] = table.concat(val, ", ") end end end export.delete_forms(data) data.title =  .. pagename .. ' (Declension ' .. pattern_display[pattern]		.. (data.extra and ", "..data.extra.."''" or "") .. ')' local res = export.show_table(data.fem2):gsub('', data) return res .. (data.category or "") end

return export