Module:amf-nominal
Jump to navigation
Jump to search
- The following documentation is located at Module:amf-nominal/documentation. [edit] Categories were auto-generated by Module:module categorization. [edit]
- Useful links: subpage list • links • transclusions • testcases • sandbox
Implements {{amf-ndecl}}
. Inflection data stored in Module:amf-nominal/data.
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] = "—"
end
elseif data.modifier == "f" then
data.extra = "feminine only"
for _,c in ipairs(cases) do
data["m_"..c] = "—"
end
elseif data.modifier == "sg" then
data.extra = "singular only"
data.category = "[[Category:"..amf:getFullName().." singularia tantum|"..amf:makeSortKey(data.pagename).."]]"
for _,c in ipairs(cases) do
data["pl_"..c] = "—"
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 = '<i>' .. pagename .. '</i> (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