local export = {}
local m_links = require("Module:links")
local lang = require("Module:languages").getByCode("pqm")
-- defined below
local format_links, categorize
--entry point
function export.pqmdecl(frame)
local params = {
= {},
= {alias_of = 1},
= {alias_of = 1},
= {},
= {alias_of = "gender"},
= {list = true},
= {alias_of = "ending", list = true},
= {default = "half"},
= {alias_of = "mode"},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {type = "boolean", default = "true"},
= {list = true},
= {list = true},
= {type = "boolean", default = "false"}
}
local args = require("Module:parameters").process(frame:getParent().args, params)
local data = {forms = {}, endings = {}, endingForms = {}, cascade = {}}
local head = args
if head == nil or head == "" then
data.forms.title = mw.title.getCurrentTitle().text
data.forms.sg = format_links(mw.title.getCurrentTitle().text)
head = mw.title.getCurrentTitle().text
else
data.forms.title = head
data.forms.sg = format_links(head)
end
pl = args
local gender = catch(args or args, "animate")
local ending = args
if #ending > 0 then
if catch(ending:sub(-1), "-ok") == "l" then
gender = "inanimate"
end
end
if gender:lower() == "in" or gender:lower() == "inanimate" then
gender = "inanimate"
else
gender = "animate"
end
data.cascade = {pl = {}}
-- Handle additional root forms
for addKey, addVal in pairs(args) do
ending = format_ending(ending)
local firstEnding = ending
-- Decline plural
local pluralForm = addVal .. format_ending_append(firstEnding, addVal)
data.forms = format_links(pluralForm)
data.endingForms = ending
local placeholder = "pl." .. addKey
table.insert(data.cascade.pl, placeholder)
end
for key, value in pairs(ending) do
local en = catch(value, "-ok")
if en:lower() == "p" or en:lower() == "participle" then
ending = "verb participle"
end
ending = format_ending(ending)
-- Decline plural
local pluralForm = head .. format_ending_append(ending, head)
-- Replace final 'q' with 'k' if following vowel is 'u'
local enVow = ending:sub(2, 2)
if enVow == 'u' and head:sub(-1, -1) == 'q' then
pluralForm = head:sub(1, -2) .. "k" .. format_ending_append(ending, head)
end
data.forms] = format_links(pluralForm)
data.endingForms = ending
local placeholder = "pl." .. key + #args
table.insert(data.cascade.pl, placeholder)
end
-- Replace auto-declined forms
if pl ~= nil and pl ~= "" and pl ~= "—" then
data.forms = format_links(pl)
end
-- Additional plural forms
for I = 2, pl.maxindex do
if pl ~= nil then
local placeholder = "pl." .. #data.endingForms + I - 1 + #args
data.forms = format_links(pl)
table.insert(data.cascade.pl, placeholder)
end
end
if #ending == 0 then
ending = "-ok"
end
local joinedEndings = ", " .. table.concat(ending, " or ")
if #ending > 2 then
local leadingEndings = table.concat(ending, ', ', 1, #ending - 2)
local finalTwoEndings = table.concat(ending, ' or ', #ending - 1, #ending)
joinedEndings = "; " .. leadingEndings .. ", " .. finalTwoEndings
end
local annotation = gender .. joinedEndings
if ending ~= "verb participle" then
annotation = annotation .. " plural"
end
local proximateText = ""
if gender == "animate" then
proximateText = " / proximate"
end
data.forms.annotation = ""
data.forms.proximateText = proximateText
local mode = catch(args:lower(), "half")
local infEnabled = {"pos", "loc", "dim"}
if mode == "full" and gender == "inanimate" then
infEnabled = {"pos", "loc", "dim", "abs"}
elseif mode == "full" and gender == "animate" then
infEnabled = {"obv", "pos", "loc", "dim", "abs", "abs-obv"}
elseif mode == "miniloc" or mode == "l" then
infEnabled = {"loc"}
elseif mode == "minipos" or mode == "p" then
infEnabled = {"pos"}
elseif mode == "minidim" or mode == "d" then
infEnabled = {"dim"}
elseif mode == "minilocdim" or mode == "ld" or mode == "dl" then
infEnabled = {"loc", "dim"}
elseif mode == "miniposdim" or mode == "pd" or mode == "dp" then
infEnabled = {"pos", "dim"}
elseif mode == "miniobv" or mode == "o" then
infEnabled = {"obv"}
elseif mode == "voc" or mode == "v" then
infEnabled = {"pos", "loc", "dim", "voc"}
elseif mode == "mini" then
infEnabled = {}
end
for key, formCode in pairs(infEnabled) do
data.cascade = {sg = {}, pl = {}}
local sgForms = args
local plForms = args
data.forms = "—"
data.forms = "—"
if args == false then
for I = 1, sgForms.maxindex do
if sgForms ~= nil then
local placeholder = formCode .. ".sg." .. I
data.forms = format_links(sgForms)
table.insert(data.cascade.sg, placeholder)
end
end
for I = 1, plForms.maxindex do
if plForms ~= nil then
local placeholder = formCode .. ".pl." .. I
data.forms = format_links(plForms)
table.insert(data.cascade.pl, placeholder)
end
end
else
-- Handle additional root forms
for addKey, addVal in pairs(args) do
data.forms = format_links(autoDeclineSg(addVal, formCode, data.endingForms, gender, args))
data.forms = format_links(autoDeclinePl(addVal, formCode, data.endingForms, gender, args))
table.insert(data.cascade.sg, formCode .. ".sg." .. addKey)
table.insert(data.cascade.pl, formCode .. ".pl." .. addKey)
end
local addLen = #args
-- Auto-decline
for endingKey, endingVal in pairs(data.endingForms) do
-- Prevent diminutive duplicates
if formCode ~= "dim" or endingKey == 1 then
data.forms = format_links(autoDeclineSg(head, formCode, endingVal, gender, args))
data.forms = format_links(autoDeclinePl(head, formCode, endingVal, gender, args))
table.insert(data.cascade.sg, formCode .. ".sg." .. endingKey + addLen)
table.insert(data.cascade.pl, formCode .. ".pl." .. endingKey + addLen)
end
end
-- Replace auto-declined forms
if sgForms ~= nil and sgForms ~= "" and sgForms ~= "—" then
data.forms = format_links(sgForms)
end
if plForms ~= nil and plForms ~= "" and plForms ~= "—" then
data.forms = format_links(plForms)
end
-- Disable forms
if sgForms == "d" or sgForms == "disable" then
data.cascade.sg = {formCode .. ".sg.1"}
data.forms = "—"
end
if plForms == "d" or plForms == "disable" then
data.cascade.pl = {formCode .. ".pl.1"}
data.forms = "—"
end
-- Additional forms
local edgFormLen = #data.endingForms
for I = 2, sgForms.maxindex do
if sgForms ~= nil then
local placeholder = formCode .. ".sg." .. edgFormLen + I - 1 + addLen
data.forms = format_links(sgForms)
table.insert(data.cascade.sg, placeholder)
end
end
for I = 2, plForms.maxindex do
if plForms ~= nil then
local placeholder = formCode .. ".pl." .. edgFormLen + I - 1 + addLen
data.forms = format_links(plForms)
table.insert(data.cascade.pl, placeholder)
end
end
end
local sgInit = data.forms
local plInit = data.forms
if sgInit == "—" and plInit == "—" then
for infKey, infVal in pairs(infEnabled) do
if infVal == formCode then
infEnabled = nil
end
end
end
end
-- Custom diminutive forms replace existing ones
if #args > 0 then
data.cascade.sg = {}
data.cascade.pl = {}
end
-- Diminutives
for dimKey, dimVal in pairs(args) do
local commonDimMarkers = {"oss", "uhs", "s", "ehs", "es", "ahs"}
local vow = {'a', 'e', 'i', 'o', 'u'}
local dimHead = head
if contains(commonDimMarkers, dimVal) then
if head:sub(-1, -1) == "u" and dimVal:sub(1, 1) == "u" then
dimVal = dimVal:sub(2)
-- Replace final 'q' with 'k' if following vowel is 'u'
elseif dimVal:sub(1, 1) == 'u' and head:sub(-1, -1) == 'q' then
dimHead = dimHead:sub(1, -2) .. 'k'
elseif contains(vow, head:sub(-1, -1)) then
dimVal = "w" .. dimVal
end
dimVal = dimHead .. dimVal
end
data.forms = format_links(autoDeclineSg(dimVal, 'dim', 'nil', gender, args))
data.forms = format_links(autoDeclinePl(dimVal, 'dim', 'nil', gender, args))
table.insert(data.cascade.sg, "dim.sg." .. dimKey)
table.insert(data.cascade.pl, "dim.pl." .. dimKey)
end
infEnabled = removeNil(infEnabled)
data.infEnabled = infEnabled
return export.make_table(data)
end
function export.make_table(data)
local colors = {dark = "#C0CFE4", light = "#F8F9FA"}
local result = {}
local tail = [=[
|}
</div>
</div>]=]
table.insert(result, [=[
<div class="NavFrame" style="width: 100%">
<div class="NavHead">Declension of ''{title}'' {annotation}</div>
<div class="NavContent">
{|style="width:100%; margin: 0px; border-collapse: separate; border-spacing: 2px; background: {light}" class="inflection-table" cellpadding=8
! style="width: 30%; background-color: {dark}" |
! style="width: 35%; background-color: {dark}; text-align: left; padding: 5px 8px 5px 8px" | singular
! style="width: 35%; background-color: {dark}; text-align: left; padding: 5px 8px 5px 8px" | plural
|- style="background: {light}; text-align: left" |
! style="background: {dark}; text-align: left; padding: 5px 8px 5px 8px" | unmarked{proximateText}
| '''{sg}'''
]=])
table.insert(result, "| ")
for key, value in pairs(data.cascade.pl) do
table.insert(result, "{" .. value .. "}")
if key < #data.cascade.pl then
table.insert(result, " / ")
end
end
for key, value in pairs(data.infEnabled) do
local formatting = [=[
|- style="background: {light}; text-align: left" |
! style="background: {dark}; text-align: left; padding: 5px 8px 5px 8px" | {form}
]=]
local formName = format_noun_form(value)
table.insert(result, (formatting:gsub("{form}", formName)))
table.insert(result, "| ")
-- Singular form(s)
for rock, stone in pairs(data.cascade.sg) do
table.insert(result, "{" .. stone .. "}")
if rock < #data.cascade.sg then
table.insert(result, " / ")
end
end
table.insert(result, "\n| ")
-- Plural form(s)
for rock, stone in pairs(data.cascade.pl) do
table.insert(result, "{" .. stone .. "}")
if rock < #data.cascade.pl then
table.insert(result, " / ")
end
end
end
-- Tail
table.insert(result, tail)
return (string.gsub(table.concat(result), "{(+)}",
function(code)
return data.forms or colors
end))
end
function format_links(link)
if (link == nil or link == "" or link == "—") then
return "—"
else
return m_links.full_link({lang = lang, term = link})
end
end
function format_ending(ending)
if ending == "" or ending == nil then
ending = "-ok"
elseif ending:lower() == "iyik-e" then
ending = "-iyik (e)"
elseif ending:lower() == "iyik-i" then
ending = "-iyik (i)"
elseif ending:lower() == "iyik" then
ending = "-iyik"
elseif ending:lower() == "iyil-e" then
ending = "-iyil (e)"
elseif ending:lower() == "iyil-i" then
ending = "-iyil (i)"
elseif ending:lower() == "iyil" then
ending = "-iyil"
elseif ending:lower() == "verb participle" then
ending = "verb participle"
else
ending = "-" .. ending:gsub("-", ""):lower()
end
return ending
end
-- Obstruent consonants (save for 'h')
local obs = {'p', 't', 'c', 'k', 'q', 's'}
-- Vowels ('eh' is not phonemic)
local vow = {'a', 'e', 'i', 'o', 'u'}
function format_ending_append(ending, head)
local lastLetter = head:sub(-1, -1)
local enVow = ending:sub(2, 2)
if ending == "" or ending == nil then
ending = "ok"
elseif ending:lower() == "-iyik (e)" then
ending = "iyik"
elseif ending:lower() == "-iyik (i)" then
ending = "iyik"
elseif ending:lower() == "-iyik" then
ending = "iyik"
elseif ending:lower() == "-iyil (e)" then
ending = "iyil"
elseif ending:lower() == "-iyil (i)" then
ending = "iyil"
elseif ending:lower() == "-iyil" then
ending = "iyil"
elseif ending:lower() == "verb participle" then
ending = ""
else
ending = ending:gsub("-", ""):lower()
end
-- 'u' merges morphophonologically
if enVow == 'u' and lastLetter == 'u' then
ending = ending:sub(2)
-- Other adjacent vowels are separated by 'w'
elseif contains(vow, enVow) and contains(vow, lastLetter) then
ending = 'w' .. ending
end
return ending
end
function format_noun_form(code)
if code == "obv" then
code = "obviative"
elseif code == "pos" then
code = "possessed"
elseif code == "loc" then
code = "locative"
elseif code == "dim" then
code = "diminutive"
elseif code == "abs" then
code = "absentative"
elseif code == "abs-obv" then
code = "abs. obviative"
elseif code == "voc" then
code = "vocative"
else
code = "ERROR. Unknown noun form."
end
return code
end
function autoDeclineSg(head, code, edg, anim, drop)
local declined = "—"
local enVow = edg:sub(2, 2)
local lastLetter = head:sub(-1, -1)
if edg == '-iyik (e)' then
enVow = 'e'
end
-- Possessed
if code == "pos" then
local pre = ''
-- Starting two letters form a consonant cluster: prepend "u"
if contains(obs, head:sub(1, 1)) and contains(obs, head:sub(2, 2)) then
pre = "u"
-- Starting letter is obstruent: prepend "'"
elseif contains(obs, head:sub(1, 1)) then
pre = "'"
-- Starting letter is vowel: prepend "'t"
elseif contains(vow, head:sub(1, 1)) then
pre = "'t"
-- Starting letter is "'": prepend "u"
elseif head:sub(1, 1) == "'" then
pre = "u"
head = head:sub(2)
end
local post = ''
-- Animate singular -ol ending
if anim == 'animate' then
post = 'ol'
end
-- Replace final 'q' with 'k' if following vowel is 'u'
if enVow == 'u' and lastLetter == 'q' then
head = head:sub(1, -2) .. 'k'
-- 'u' merges morphophonologically
elseif enVow == 'u' and lastLetter == 'u' then
enVow = ''
-- Other adjacent vowels are separated by 'w'
elseif contains(vow, enVow) and contains(vow, lastLetter) then
enVow = 'w' .. enVow
end
local connector = enVow .. 'm'
if drop == true then
connector = ""
end
declined = pre .. head .. connector .. post
elseif code == "loc" then
if anim == 'inanimate' and edg == '-uwol' then
enVow = 'uwo'
end
-- Replace final 'q' with 'k' if following vowel is 'u'
if enVow == 'u' and lastLetter == 'q' then
head = head:sub(1, -2) .. 'k'
-- 'u' merges morphophonologically
elseif enVow == 'u' and lastLetter == 'u' then
enVow = ''
-- Other adjacent vowels are separated by 'w'
elseif contains(vow, enVow) and contains(vow, lastLetter) then
enVow = 'w' .. enVow
end
declined = head .. enVow .. 'k'
-- Diminutive
elseif code == "dim" then
if contains(vow, lastLetter) then
head = head .. 'w'
end
declined = head .. 'is'
-- Absentative
elseif code == "abs" then
local post = enVow .. "w"
if enVow == "u" then
post = "u"
elseif enVow == "o" then
post = ""
end
if lastLetter == "u" and enVow == "u" then
post = ""
elseif contains(vow, lastLetter) then
head = head .. 'w'
end
declined = head .. post
-- Obviative
elseif code == "obv" then
-- Replace final 'q' with 'k' if following vowel is 'u'
if enVow == 'u' and lastLetter == 'q' then
head = head:sub(1, -2) .. 'k'
end
declined = head .. format_ending_append(edg, head):sub(1, -2) .. "l"
-- Absentative obviative
elseif code == "abs-obv" then
local post = enVow .. "kkol"
if enVow == "o" then
post = "kol"
end
if lastLetter == "u" and enVow == "u" then
post = "kkol"
elseif contains(vow, lastLetter) then
head = head .. "w"
end
declined = head .. post
end
return declined
end
function autoDeclinePl(head, code, edg, anim, drop)
local declined = "—"
local enVow = edg:sub(2, 2)
local lastLetter = head:sub(-1, -1)
if edg == '-iyik (e)' then
enVow = 'e'
end
-- Possessed
if code == "pos" then
local pre = ''
-- Starting two letters form a consonant cluster: prepend "u"
if contains(obs, head:sub(1, 1)) and contains(obs, head:sub(2, 2)) then
pre = "u"
-- Starting letter is obstruent: prepend "'"
elseif contains(obs, head:sub(1, 1)) then
pre = "'"
-- Starting letter is vowel: prepend "'t"
elseif contains(vow, head:sub(1, 1)) then
pre = "'t"
-- Starting letter is "'": prepend "u"
elseif head:sub(1, 1) == "'" then
pre = "u"
head = head:sub(2)
end
local post = 'ol'
-- Animate plural has no -ol ending
if anim == 'animate' then
post = ''
end
-- Replace final 'q' with 'k' if following vowel is 'u'
if enVow == 'u' and lastLetter == 'q' then
head = head:sub(1, -2) .. 'k'
-- 'u' merges morphophonologically
elseif enVow == 'u' and lastLetter == 'u' then
enVow = ''
-- Other adjacent vowels are separated by 'w'
elseif contains(vow, enVow) and contains(vow, lastLetter) then
enVow = 'w' .. enVow
end
local connector = enVow .. 'm'
if drop == true then
connector = ""
end
declined = pre .. head .. connector .. post
elseif code == "loc" then
if enVow == 'o' or enVow == 'i' then
enVow = ''
end
if enVow == 'u' then
enVow = 'uw'
-- 'q' replaces 'uw' before 'i'
if lastLetter == 'q' then
enVow = ''
elseif lastLetter == 'u' then
enVow = 'w'
end
elseif contains(vow, enVow) and contains(vow, lastLetter) then
enVow = 'w' .. enVow .. 'w'
elseif enVow == 'i' then
enVow = ''
elseif contains(vow, enVow) then
enVow = enVow .. 'w'
end
declined = head .. enVow .. 'ihkuk'
-- Diminutive
elseif code == "dim" then
if contains(vow, lastLetter) then
head = head .. 'w'
end
declined = head .. 'isol'
if anim == 'animate' then
declined = head .. 'isok'
end
-- Absentative and absentative obviative
elseif code == "abs" or code == "abs-obv" then
local post = enVow .. "kko"
local animacyEnding = "l"
if anim == 'animate' then
animacyEnding = "kk"
end
if enVow == "o" then
post = "ko"
end
if lastLetter == "u" and enVow == "u" then
post = "kko"
elseif contains(vow, lastLetter) then
head = head .. "w"
end
declined = head .. post .. animacyEnding
-- Obviative
elseif code == "obv" then
local post = enVow
if enVow == "o" then
post = ""
elseif enVow == "i" or enVow == "e" then
post = "iyi"
end
-- Replace final 'q' with 'k' if following vowel is 'u'
if enVow == 'u' and lastLetter == 'q' then
head = head:sub(1, -2) .. 'k'
elseif lastLetter == "u" and enVow == "u" then
post = ""
elseif contains(vow, lastLetter) then
head = head .. "w"
end
declined = head .. post
end
return declined
end
function catch(arg, default)
if (arg == nil or (arg:gsub("%s+", "")) == "") then
return default
else
return (arg:gsub("%s+", ""))
end
end
function split(str)
local lines = {}
for s in str:gmatch("+") do
table.insert(lines, s)
end
return lines
end
function contains(set, key)
for k0, val in pairs(set) do
if val == key then
return true
end
end
return false
end
function removeNil(t)
local temp = {}
for I = 1, #t do
if t ~= nil then table.insert(temp, t) end
end
return temp
end
return export