local export = {}
local m_str_utils = require("Module:string utilities")
local m_table = require("Module:table")
local m_com = require("Module:eu-common")
local lang = require("Module:languages").getByCode("eu")
local rfind = m_str_utils.find
local rsub = m_str_utils.gsub
local rsplit = m_str_utils.split
----------------------------------------------
----------------------------------------------
local function generate_from_basic_table(basic_code, nums_remove, cleanup_indef, headers)
--Generate a table by modifying the basic tables
--we remove:
-- slots with data-accel-col=""
-- headers present in the variable <headers>
--if cleanup_indef is not nil, we show a nil value in the specified rows of the indefinite column
local clean_lines = {}
local search_headers = m_table.listToSet(headers)
for line in basic_code:gmatch("+") do
if not rfind(line, 'data%-accel%-col=""') and not search_headers then
if cleanup_indef then
if rfind(line, 'data%-accel%-col="1"') and not m_com.rfind_multiple(line, cleanup_indef) then
table.insert(clean_lines, "| —")
else
table.insert(clean_lines, line)
end
else
table.insert(clean_lines, line)
end
end
end
return table.concat(clean_lines, "\n") .. "\n"
end
-- Table-generating functions
-- st: default table (indefinite, singular and plural columns; single animacy)
-- st-sg: two columns (indefinite and singular) and a single animacy. The instrumental indefinite is shown.
-- st-pl: two columns (indefinite and plural) and a single animacy. All indefinite forms are shown.
-- st-both: same as before, but rows for two animacies (check ], ])
-- both-pl: two columns (indefinite and plural) and rows for two animacies (e.g bizpahiru FIXME: needs checking).
-- Tables for proper nouns:
-- ind: one column (indefinite) and a single animacy
-- ind-both: one column (indefinite) and rows for two animacies
-- ind-sg: two columns (indefinite and singular) and a single animacy. The indefinite column shows the absolutive, partitive and prolative only.
-- ind-pl: two columns (indefinite and singular) and a single animacy. The indefinite column shows the absolutive, partitive and prolative only.
local function make_table(data, tbl_type)
local function repl(param)
local accel = true
local no_store = false
if param == "info" then
return mw.getContentLanguage():ucfirst(data.info or "")
elseif string.sub(param, 1, 1) == "!" then
no_store = true
param = string.sub(param, 2)
elseif string.sub(param, 1, 1) == "#" then
accel = false
param = string.sub(param, 2)
end
local forms = data.forms
if not forms then
return "—"
end
local ret = {}
for key, subform in ipairs(forms) do
table.insert(ret, require("Module:links").full_link({lang = lang, term = subform, accel = accel and {form = param, lemma = data.lemma, no_store = no_store} or nil}))
end
return table.concat(ret, "<br/>")
end
local wikicode = mw.getCurrentFrame():expandTemplate{
title = 'inflection-table-top',
args = {
title = '{{{info}}}',
tall = 'yes',
palette = 'blue'
}
}
--Here we list the two most basic tables (three columns, all tenses). We will generate other tables from them.
basic_table = [=[
!
! indefinite
! singular
! plural
|-
! absolutive
| data-accel-col="1" | {{{absv|indef}}}
| data-accel-col="2" | {{{absv|s}}}
| data-accel-col="3" | {{{absv|p}}}
|-
! ergative
| data-accel-col="1" | {{{erg|indef}}}
| data-accel-col="2" | {{{erg|s}}}
| data-accel-col="3" | {{{erg|p}}}
|-
! dative
| data-accel-col="1" | {{{dat|indef}}}
| data-accel-col="2" | {{{dat|s}}}
| data-accel-col="3" | {{{dat|p}}}
|-
! genitive
| data-accel-col="1" | {{{gen|indef}}}
| data-accel-col="2" | {{{gen|s}}}
| data-accel-col="3" | {{{gen|p}}}
|-
! comitative
| data-accel-col="1" | {{{com|indef}}}
| data-accel-col="2" | {{{com|s}}}
| data-accel-col="3" | {{{com|p}}}
|-
! causative
| data-accel-col="1" | {{{caus|indef}}}
| data-accel-col="2" | {{{caus|s}}}
| data-accel-col="3" | {{{caus|p}}}
|-
! benefactive
| data-accel-col="1" | {{{ben|indef}}}
| data-accel-col="2" | {{{ben|s}}}
| data-accel-col="3" | {{{ben|p}}}
|-
! instrumental
| data-accel-col="1" | {{{ins|indef}}}
| data-accel-col="2" | {{{ins|s}}}
| data-accel-col="3" | {{{ins|p}}}
|-
! inessive
| data-accel-col="1" | {{{ine|indef}}}
| data-accel-col="2" | {{{ine|s}}}
| data-accel-col="3" | {{{ine|p}}}
|-
! locative
| data-accel-col="1" | {{{loc|indef}}}
| data-accel-col="2" | {{{loc|s}}}
| data-accel-col="3" | {{{loc|p}}}
|-
! allative
| data-accel-col="1" | {{{all|indef}}}
| data-accel-col="2" | {{{all|s}}}
| data-accel-col="3" | {{{all|p}}}
|-
! terminative
| data-accel-col="1" | {{{ter|indef}}}
| data-accel-col="2" | {{{ter|s}}}
| data-accel-col="3" | {{{ter|p}}}
|-
! directive
| data-accel-col="1" | {{{directive|indef}}}
| data-accel-col="2" | {{{directive|s}}}
| data-accel-col="3" | {{{directive|p}}}
|-
! destinative
| data-accel-col="1" | {{{destinative|indef}}}
| data-accel-col="2" | {{{destinative|s}}}
| data-accel-col="3" | {{{destinative|p}}}
|-
! ablative
| data-accel-col="1" | {{{abl|indef}}}
| data-accel-col="2" | {{{abl|s}}}
| data-accel-col="3" | {{{abl|p}}}
|-
! partitive
| data-accel-col="1" | {{{par|indef}}}
| data-accel-col="2" | {{{par|s}}}
| data-accel-col="3" | {{{par|p}}}
|-
! prolative
| data-accel-col="1" | {{{pro|indef}}}
| data-accel-col="2" | {{{pro|s}}}
| data-accel-col="3" | {{{pro|p}}}
]=]
basic_table_both_anims = [=[
! colspan="2"|
! indefinite
! singular
! plural
|-
! colspan="2"| absolutive
| data-accel-col="1" | {{{absv|indef}}}
| data-accel-col="2" | {{{absv|s}}}
| data-accel-col="3" | {{{absv|p}}}
|-
! colspan="2"| ergative
| data-accel-col="1" | {{{erg|indef}}}
| data-accel-col="2" | {{{erg|s}}}
| data-accel-col="3" | {{{erg|p}}}
|-
! colspan="2"| dative
| data-accel-col="1" | {{{dat|indef}}}
| data-accel-col="2" | {{{dat|s}}}
| data-accel-col="3" | {{{dat|p}}}
|-
! colspan="2"| genitive
| data-accel-col="1" | {{{gen|indef}}}
| data-accel-col="2" | {{{gen|s}}}
| data-accel-col="3" | {{{gen|p}}}
|-
! colspan="2"| comitative
| data-accel-col="1" | {{{com|indef}}}
| data-accel-col="2" | {{{com|s}}}
| data-accel-col="3" | {{{com|p}}}
|-
! colspan="2"| causative
| data-accel-col="1" | {{{caus|indef}}}
| data-accel-col="2" | {{{caus|s}}}
| data-accel-col="3" | {{{caus|p}}}
|-
! colspan="2"| benefactive
| data-accel-col="1" | {{{ben|indef}}}
| data-accel-col="2" | {{{ben|s}}}
| data-accel-col="3" | {{{ben|p}}}
|-
! colspan="2"| instrumental
| data-accel-col="1" | {{{ins|indef}}}
| data-accel-col="2" | {{{ins|s}}}
| data-accel-col="3" | {{{ins|p}}}
|-
! rowspan="2"| inessive
! class="secondary" | anim
| data-accel-col="1" | {{{ine|animate|indef}}}
| data-accel-col="2" | {{{ine|animate|s}}}
| data-accel-col="3" | {{{ine|animate|p}}}
|-
! class="secondary" | inan
| data-accel-col="1" | {{{ine|inanimate|indef}}}
| data-accel-col="2" | {{{ine|inanimate|s}}}
| data-accel-col="3" | {{{ine|inanimate|p}}}
|-
! rowspan="2"| locative
! class="secondary" | anim
| data-accel-col="1" | {{{loc|animate|indef}}}
| data-accel-col="2" | {{{loc|animate|s}}}
| data-accel-col="3" | {{{loc|animate|p}}}
|-
! class="secondary" | inan
| data-accel-col="1" | {{{loc|inanimate|indef}}}
| data-accel-col="2" | {{{loc|inanimate|s}}}
| data-accel-col="3" | {{{loc|inanimate|p}}}
|-
! rowspan="2"| allative
! class="secondary" | anim
| data-accel-col="1" | {{{all|animate|indef}}}
| data-accel-col="2" | {{{all|animate|s}}}
| data-accel-col="3" | {{{all|animate|p}}}
|-
! class="secondary" | inan
| data-accel-col="1" | {{{all|inanimate|indef}}}
| data-accel-col="2" | {{{all|inanimate|s}}}
| data-accel-col="3" | {{{all|inanimate|p}}}
|-
! rowspan="2"| terminative
! class="secondary" | anim
| data-accel-col="1" | {{{ter|animate|indef}}}
| data-accel-col="2" | {{{ter|animate|s}}}
| data-accel-col="3" | {{{ter|animate|p}}}
|-
! class="secondary" | inan
| data-accel-col="1" | {{{ter|inanimate|indef}}}
| data-accel-col="2" | {{{ter|inanimate|s}}}
| data-accel-col="3" | {{{ter|inanimate|p}}}
|-
! rowspan="2"| directive
! class="secondary" | anim
| data-accel-col="1" | {{{directive|animate|indef}}}
| data-accel-col="2" | {{{directive|animate|s}}}
| data-accel-col="3" | {{{directive|animate|p}}}
|-
! class="secondary" | inan
| data-accel-col="1" | {{{directive|inanimate|indef}}}
| data-accel-col="2" | {{{directive|inanimate|s}}}
| data-accel-col="3" | {{{directive|inanimate|p}}}
|-
! rowspan="2"| destinative
! class="secondary" | anim
| data-accel-col="1" | {{{destinative|animate|indef}}}
| data-accel-col="2" | {{{destinative|animate|s}}}
| data-accel-col="3" | {{{destinative|animate|p}}}
|-
! class="secondary" | inan
| data-accel-col="1" | {{{destinative|inanimate|indef}}}
| data-accel-col="2" | {{{destinative|inanimate|s}}}
| data-accel-col="3" | {{{destinative|inanimate|p}}}
|-
! rowspan="2"| ablative
! class="secondary" | anim
| data-accel-col="1" | {{{abl|animate|indef}}}
| data-accel-col="2" | {{{abl|animate|s}}}
| data-accel-col="3" | {{{abl|animate|p}}}
|-
! class="secondary" | inan
| data-accel-col="1" | {{{abl|inanimate|indef}}}
| data-accel-col="2" | {{{abl|inanimate|s}}}
| data-accel-col="3" | {{{abl|inanimate|p}}}
|-
! colspan="2"| partitive
| data-accel-col="1" | {{{par|indef}}}
| data-accel-col="2" | {{{par|s}}}
| data-accel-col="3" | {{{par|p}}}
|-
! colspan="2"| prolative
| data-accel-col="1" | {{{pro|indef}}}
| data-accel-col="2" | {{{pro|s}}}
| data-accel-col="3" | {{{pro|p}}}
]=]
--generate the table code
if tbl_type == "st" then
wikicode = wikicode .. basic_table
elseif tbl_type == "st-both" then
wikicode = wikicode .. basic_table_both_anims
elseif tbl_type == "both-pl" then
wikicode = wikicode .. generate_from_basic_table(basic_table_both_anims, "2", nil, {"! singular"})
elseif tbl_type == "st-sg" then
wikicode = wikicode .. generate_from_basic_table(basic_table, "3", {"absv", "ins", "par", "pro"}, {"! plural"})
elseif tbl_type == "st-pl" then
wikicode = wikicode .. generate_from_basic_table(basic_table, "2", nil, {"! singular"})
elseif tbl_type == "ind" then
wikicode = wikicode .. generate_from_basic_table(basic_table, "23", nil, {"! singular", "! plural"})
elseif tbl_type == "ind-sg" then
wikicode = wikicode .. generate_from_basic_table(basic_table, "3", {"absv", "par", "pro"}, {"! plural"})
elseif tbl_type == "ind-pl" then
wikicode = wikicode .. generate_from_basic_table(basic_table, "2", {"absv", "par", "pro"}, {"! singular"})
elseif tbl_type == "ind-both" then
wikicode = wikicode .. generate_from_basic_table(basic_table_both_anims, "23", nil, {"! singular", "! plural"})
else
error("Unsupported table type: '" .. tbl_type .. "'.")
end
wikicode = wikicode .. mw.getCurrentFrame():expandTemplate{ title = 'inflection-table-bottom', args = {notes = data.notes}}
return mw.ustring.gsub(wikicode, "{{{?(+)}}}", repl) .. require("Module:utilities").format_categories(data.categories, lang)
end --normal table
-- Inflection functions (proper nouns)
----------------------------------------------
----------------------------------------------
local function get_overrides(s)
if type(s) ~= "string" then
return {}
end
local parts, seen = rsplit(s, "%."), {}
for _, part in ipairs(parts) do
if seen then
error("Duplicate property: '" .. part .. "'.")
end
seen = true
end
return m_table.listToSet(parts)
end
local function get_animacy(overrides)
local anim_count = 0
local valid_anim = m_table.listToSet({"an", "in", "both"})
local result
for part, _ in pairs(overrides) do
if valid_anim then
anim_count = anim_count + 1
result = part
end
end
if anim_count ~= 1 then
error("Exactly one of 'an', 'in', or 'both' must be provided")
end
return result
end
function export.generate_forms(noun, animacy, overrides, pos)
local data = {
forms = {},
info = "Declension of ",
categories = {},
notes = "",
}
local lemma = noun
local definiteness = ""
--Handle overrides
local e_dip_ins = (overrides or overrides) and "e" or "" --monosyllabic terms ending in a dipthong get an epenthetic -e- in the indefinite instrumental (deiez, not *deiz)
local e_gau = overrides and "e" or "" --gau (and its derivatives) get an epenthetic -e- in the singular local cases
--Proper-noun overrides
if overrides then
if not rfind(noun, "a$") then
lemma = noun .. "a"
definiteness = " def-sg"
end
elseif overrides then
if not rfind(noun, "a$") then
lemma = noun .. "ak"
else
lemma = lemma .. "k"
end
definiteness = " def-pl"
end
--Declension info. FIXME: orthographic vowel/consonant overrides missing
local anim_words = {an = "anim", = "inan", both = "anim/inan", adj = "adj"}
data.info = data.info .. require("Module:links").full_link({lang = lang, alt = lemma}, "term") .. " <small>(" .. anim_words .. definiteness .. " "
--plural/singular only?
if overrides then
data.info = data.info .. "sg-only "
elseif overrides then
data.info = data.info .. "pl-only "
end
--stem type. Note that acronyms that aren't spelled out are treated like any other word (but FAGOR -> FAGORek, not *FAGORrek)
local ending_A = false
local ending_JKXZ = false
if rfind(noun, "$") and overrides then
data.info = data.info .. "a-stem" --initialisms whose final letter ends in -a. We include <jkxz> as they might occur in mixed-case initialisms.
ending_JKXZ = true
elseif rfind(noun, "ah?$") then
data.info = data.info .. "a-stem"
elseif rfind(noun, "A$") then
data.info = data.info .. "a-stem"
ending_A = true
elseif rfind(noun, "h?$") or overrides then --word-final w and y are treated as vowels. Word-final <h> is mute.
data.info = data.info .. "V-stem"
elseif overrides then
data.info = data.info .. "ɾ-stem"
else
data.info = data.info .. "C-stem"
end
--insert notes if needed
local notes_data = {}
local word_acr = overrides and "a phonetic acronym" or "an initialism"
if overrides or overrides then
data.info = data.info .. "<sup>1</sup>"
if pos ~= "proper" then
if ending_A then
table.insert(notes_data,
"This is " .. word_acr .. " ending in " .. mw.getCurrentFrame():expandTemplate{title = 'IPAchar', args = {"/a/"}} .. ". In plural forms the " ..
mw.getCurrentFrame():expandTemplate{title = 'angbr', args = {"A"}} .. " is retained in text and speech.")
else
if overrides then
table.insert(notes_data, "This is a phonetic acronym. The declension is regular.")
else
if ending_JKXZ then
table.insert(notes_data, "This is an initialism whose final letter ends in " .. mw.getCurrentFrame():expandTemplate{title = 'IPAchar', args = {"/a/"}} ..
". The definite article is written (but not pronounced) separately.")
else
table.insert(notes_data, "This is an initialism whose final letter ends in a vowel other than " .. mw.getCurrentFrame():expandTemplate{title = 'IPAchar', args = {"/a/"}} ..
". The declension is regular.")
end
end
end
else
table.insert(notes_data, "This is " .. word_acr .. ". The declension is regular")
end
else
if rfind(noun, "h$") or rfind(noun, "$") or overrides then
table.insert(notes_data, "Optionally, the case suffixes be separated from the root with a hyphen.")
data.info = data.info .. "<sup>1</sup>"
end
if rfind(noun, "ah$") and pos ~= "proper" then
table.insert(notes_data, "The article is added separately in the singular and the " .. mw.getCurrentFrame():expandTemplate{title = 'angbr', args = {"-ah"}} .. " is kept in the plural.")
data.info = data.info .. "<sup>2</sup>"
end
end
data.info = data.info .. ")</small>"
--Epenthetic e and r
local e, r, a_s = "e", "r", "a"
if rfind(noun, "h?$") or overrides then
e = ""
else
r = ""
end
-- This is needed in acronyms like PDA (PDA + -a = PDA, not *PDAa)
if rfind(noun, "A$") then
a_s = ""
end
--add base form and prolative before changing -r to -rr
data.forms = {noun}
if rfind(noun, "t$") then
data.forms = {(rsub(noun, "t()$", "%1tzat"))}
else
data.forms = {noun .. "tzat"}
end
--double r if needed
if rfind(noun, "r$") and not (overrides or overrides) then
noun = noun .. "r"
end
--nouns in -a
local base_def = noun
if rfind(noun, "a$") then
base_def = rsub(noun, "a$", "")
end
--nouns with compulsory hyphen
if overrides then
noun = noun .. "-"
end
--absolutive
data.forms = {base_def .. a_s}
data.forms = {base_def .. a_s .. "k"}
--ergative
data.forms = {noun .. e .. "k"}
data.forms = {base_def .. a_s .. "k"}
data.forms = {base_def .. "ek"}
--dative
data.forms = {noun .. r .. "i"}
data.forms = {base_def .. a_s .. "ri"}
data.forms = {base_def .. "ei"}
--comitative
data.forms = {noun .. r .. "ekin"}
data.forms = {base_def .. a_s .. "rekin"}
data.forms = {base_def .. "ekin"}
--instrumental
data.forms = {noun .. e .. e_dip_ins .. "z"}
data.forms = {base_def .. a_s .. "z"}
data.forms = {base_def .. "ez"}
--add inanimate/animate flags
local an_flag
local in_flag = (animacy == "both") and "|inanimate" or ""
single_anim_cases = m_table.listToSet({"gen", "caus", "ben"})
--cases derived from the genitive (plus the genitive itself)
local gen_cases = {gen = "", caus = "gatik", ben = "tzat", ine = "gan", all = "gana", ter = "ganaino", directive = "ganantz", destinative = "ganako", abl = "gandik"}
for case, suf in pairs(gen_cases) do
an_flag = (animacy == "both" and not single_anim_cases) and "|animate" or ""
data.forms = {noun .. r .. "en" .. suf}
data.forms = {base_def .. a_s .. "ren" .. suf}
data.forms = {base_def .. "en" .. suf}
end
--cases with the infix -ta- in the indefinite (inanimate only). We'll overwrite the previous ones if necessary
local inan_cases = {ine = "n", loc = "ko", all = "ra", ter = "raino", directive = "rantz", destinative = "rako", abl = "tik"}
local altform_cases = m_table.listToSet({"loc", "all", "ter", "directive", "destinative", "abl"})
local cases_e_last = m_table.listToSet({"abl", "loc"})
local a
local ta = (pos == "proper") and "" or "ta"
if animacy ~= "an" then
for case, suf in pairs(inan_cases) do
a = (case == "ine" and not rfind(noun, "$")) and "a" or ""
data.forms = {noun .. e .. ta .. suf}
data.forms = {noun .. e .. e_gau .. a .. suf}
data.forms = {base_def .. "eta" .. suf}
-- Place nouns ending in certain consonants can have forms with and without -e-
if overrides and altform_cases then
local alt_loc_base = rsub(noun, "rr$", "r")
local form_w_e = noun .. e .. suf
local form_wo_e = alt_loc_base .. suf
form_wo_e = m_com.rsub_multiple(form_wo_e, {"()ko$", "()tik$", "tzko", "tztik"}, {"%1go", "%1dik", "zko", "ztik"})
if cases_e_last then
data.forms = {form_wo_e, form_w_e}
elseif rfind(noun, "r$") then --only in words like Eibar, Elgoibar...
data.forms = {form_w_e, form_wo_e}
end
end
end
end
--partitive
data.forms = {noun .. r .. "ik"}
data.notes = "<small>" .. m_com.generate_notes_text(notes_data) .. "</small>"
return data
end
function export.main(frame)
local args = frame:getParent().args
-- add the lemma form
local base = args.pagename or mw.title.getCurrentTitle().text
local pos = frame.args.pos or nil
--overrides
local overrides = args or nil
local ort = get_overrides(overrides)
--animacy (it can be hardcoded in the case of adjectives)
local anim = frame.args.anim or get_animacy(ort)
--check for incompatible overrides
if ort and ort then
error("Can't specify 'md' and 'gau' at the same time.")
elseif ort and (ort or ort) then
error("Can't specify 'tap' together with 'md' or 'gau'.")
elseif ort and not rfind(base, "r$") then
error("Can't specify 'tap' if the word doesn't end in -r.")
elseif ort and ort then
error("Can't specify 'sg' and 'pl' at the same time.")
elseif ort and (pos ~= "proper" or anim == "an") then
error("The paramter 'loc' can only be applied to inanimate proper nouns.")
elseif ort and not rfind(base, "$") then
error("Can't specify 'loc' to words not ending in -n, -l, -r, -s, -(t)z.")
elseif ort and ort then
error("Can't specify 'ini' and 'acr' at the same time.")
end
--Proper noun overrides. If an indefinite form is manually provided, check that it is compatible with the page name
local alt_base
if pos == "proper" then
if ort or ort then
error("Can't specify 'sg' or 'pl' in proper nouns.")
end
for k, _ in pairs(ort) do
if rfind(k, "^b:") then
alt_base = rsub(k, "^b:", "")
break
end
end
--Check that the alt_base is plausible
if alt_base then
if not rfind(alt_base, "a$") and (alt_base .. "a" == base) then
ort = true
elseif not rfind(alt_base, "a$") and (alt_base .. "ak" == base) then
ort = true
-- In the following two cases, the indefinite form ends with -a, so the definite forms are the same as the indefinite ones (which we don't show)
elseif rfind(alt_base, "a$") and (alt_base == base) then --e.g. Serbiarren, Kroaziarren eta Esloveniarren Erresuma
ort = true
elseif rfind(alt_base, "a$") and (alt_base .. "k" == base) then
ort = true
else
error("The definite form '" .. base .. "' doesn't match with the indefinite form '" .. alt_base .. "'.")
end
base = alt_base
end
end
--Handle acronyms
local upper_case = "ABCÇDEFGHIJKLMNÑOPQRSTUVWXYZ"
local is_acronym = rfind(base, "$")
if is_acronym and not (ort or ort) then
error("Acronym detected")
elseif not is_acronym and (ort or ort) then -- Mixed-case acronyms get a hyphen
ort = true
end
--Generate the data
local data = export.generate_forms(base, anim, ort, pos, hyphen)
--Choose the table type
local table_type = "st"
if pos == "proper" then
table_type = "ind"
end
if anim == "both" then
table_type = table_type .. "-both"
end
if ort or ort then
table_type = table_type .. "-pl"
elseif ort or ort then
table_type = table_type .. "-sg"
end
--Make the table
return make_table(data, table_type)
end
return export