Modul:eu-noun
Penampilan
- Laman modul ini kekurangan sublaman pendokumenan. Sila cipta laman pendokumenan tersebut.
- Pautan berguna: senarai sublaman • pautan • transklusi • kes ujian • kotak pasir
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
-- Table-generating functions
-- st: default table (indefinite, singular, plural and proximal plural columns; single animacy)
-- st-sg: two columns (indefinite and singular) and a single animacy. The instrumental indefinite is shown.
-- st-pl: three columns (indefinite, plural and proximal plural) and a single animacy. All indefinite forms are shown.
-- st-both: four columns (indefinite, singular, plural and proximal plural), two animacies
-- st-both-pl: three columns (indefinite, plural and proximal plural), two animacies
-- 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 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="[nums_remove]"
-- 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("[^\r\n]+") do
if not rfind(line, 'data%-accel%-col="[' .. nums_remove .. ']"') and not search_headers[line] 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
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[param]
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, ", ")
end
local wikicode = mw.getCurrentFrame():expandTemplate{
title = 'inflection-table-top',
args = {
title = '{{{info}}}',
tall = 'yes',
palette = 'blue'
}
}
-- Basic elements for constructing the tables
local basic_table = [=[
!
! indefinite
! singular
! plural
! proximal plural
|-
]=]
local basic_table_both_anims = [=[
! colspan="2"|
! indefinite
! singular
! plural
! proximal plural
]=]
local row = [=[
|-
! TENSENAME
| data-accel-col="1" | {{{TENSEABBR|indef}}}
| data-accel-col="2" | {{{TENSEABBR|s}}}
| data-accel-col="3" | {{{TENSEABBR|p}}}
| data-accel-col="4" | {{{TENSEABBR|prox|p}}}
]=]
local row_colspan = [=[
|-
! colspan="2"| TENSENAME
| data-accel-col="1" | {{{TENSEABBR|indef}}}
| data-accel-col="2" | {{{TENSEABBR|s}}}
| data-accel-col="3" | {{{TENSEABBR|p}}}
| data-accel-col="4" | {{{TENSEABBR|prox|p}}}
]=]
local row_rowspan = [=[
|-
! rowspan="2"| TENSENAME
! class="secondary" | <abbr title="animate">anim.</abbr>
| data-accel-col="1" | {{{TENSEABBR|animate|indef}}}
| data-accel-col="2" | {{{TENSEABBR|animate|s}}}
| data-accel-col="3" | {{{TENSEABBR|animate|p}}}
| data-accel-col="4" | {{{TENSEABBR|animate|prox|p}}}
|-
! class="secondary" | <abbr title="inanimate">inan.</abbr>
| data-accel-col="1" | {{{TENSEABBR|inanimate|indef}}}
| data-accel-col="2" | {{{TENSEABBR|inanimate|s}}}
| data-accel-col="3" | {{{TENSEABBR|inanimate|p}}}
| data-accel-col="4" | {{{TENSEABBR|inanimate|prox|p}}}
]=]
--Here we generate the two most basic tables (three columns, all tenses). We will generate other tables from them.
tense_abbrs = {
{"absv", "absolutive"}, {"erg", "ergative"}, {"dat", "dative"}, {"gen", "genitive"}, {"com", "comitative"}, {"caus", "causative"}, {"ben", "benefactive"}, {"ins", "instrumental"},
{"ine", "innesive"},{"loc", "locative"}, {"all", "allative"}, {"ter", "terminative"}, {"directive", "directive"}, {"destinative", "destinative"}, {"abl", "ablative"},
{"par", "partitive"}, {"pro", "prolative"}
}
dual_tenses = m_table.listToSet({"ine", "loc", "all", "ter", "directive", "destinative", "abl"})
for _, pair in ipairs(tense_abbrs) do
basic_table = basic_table .. m_com.rsub_multiple(row, {"TENSENAME", "TENSEABBR"}, {pair[2], pair[1]})
if dual_tenses[pair[1]] then
basic_table_both_anims = basic_table_both_anims .. m_com.rsub_multiple(row_rowspan, {"TENSENAME", "TENSEABBR"}, {pair[2], pair[1]})
else
basic_table_both_anims = basic_table_both_anims .. m_com.rsub_multiple(row_colspan, {"TENSENAME", "TENSEABBR"}, {pair[2], pair[1]})
end
end
--generate the table code
local tbl_refs = {
st = basic_table,
["st-both"] = basic_table_both_anims,
["st-both-pl"] = {basic_table_both_anims, "2", nil, {"! singular"}},
["st-sg"] = {basic_table, "34", {"absv", "ins", "par", "pro"}, {"! plural", "! proximal plural"}},
["st-pl"] = {basic_table, "2", nil, {"! singular"}},
ind = {basic_table, "234", nil, {"! singular", "! plural", "! proximal plural"}},
["ind-sg"] = {basic_table, "34", {"absv", "par", "pro"}, {"! plural", "! proximal plural"}},
["ind-pl"] = {basic_table, "24", {"absv", "par", "pro"}, {"! singular", "! proximal plural"}},
["ind-both"] = {basic_table_both_anims, "234", nil, {"! singular", "! plural", "! proximal plural"}}
}
local tbl_val = tbl_refs[tbl_type]
if not tbl_val then
error("Unsupported table type: '" .. tbl_type .. "'.")
elseif type(tbl_val) == "table" then
wikicode = wikicode .. generate_from_basic_table(unpack(tbl_val))
else
wikicode = wikicode .. tbl_val
end
--Close the table
wikicode = wikicode .. mw.getCurrentFrame():expandTemplate{title = 'inflection-table-bottom', args = (data.notes and {notes = data.notes}) or nil}
return mw.ustring.gsub(wikicode, "{{{[#!]?([a-z0-9|]+)}}}", repl) .. require("Module:utilities").format_categories(data.categories, lang)
end
-- Form-generating function
function export.generate_forms(noun, animacy, overrides, pos)
local data = {
forms = {},
info = "Deklesi ",
categories = {},
notes = "",
}
local lemma = noun
local definiteness = ""
--Handle overrides
local e_dip_ins = (overrides["md"] or overrides["gau"]) and "e" or "" --monosyllabic terms ending in a dipthong get an epenthetic -e- in the indefinite instrumental (deiez, not *deiz)
local e_gau = overrides["gau"] and "e" or "" --gau (and its derivatives) get an epenthetic -e- in the singular local cases
--Proper-noun overrides
if overrides["sg-def"] then
if not rfind(noun, "a$") then
lemma = noun .. "a"
end
definiteness = " def-sg"
elseif overrides["pl-def"] 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", ["in"] = "inan", both = "anim/inan"}
local anim_word = anim_words[animacy]
if pos == "adj" then
anim_word = "kata sifat"
elseif pos == "num" then
anim_word = "kata bilangan"
elseif pos == "det" then
anim_word = "determiner"
end
data.info = data.info .. require("Module:links").full_link({lang = lang, alt = lemma}, "term") .. " <small>(" .. anim_word .. definiteness .. " "
--plural/singular only?
if overrides["sg"] then
data.info = data.info .. "sg-only "
elseif overrides["pl"] then
if pos == "det" then
data.info = data.info .. "pl "
else
data.info = data.info .. "pl-only "
end
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, "[jkxzJKXZ]$") and overrides["ini"] 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, "[eiouwyEIOUWY]h?$") or overrides["ini"] then --word-final w and y are treated as vowels. Word-final <h> is mute.
data.info = data.info .. "V-stem"
elseif overrides["tap"] then
data.info = data.info .. "ɾ-stem"
else
data.info = data.info .. "C-stem"
end
--insert notes if needed
local notes_data = {}
local note_number = 0
local function add_note_number()
note_number = note_number + 1
data.info = data.info .. "<sup>" .. tostring(note_number) .. "</sup>"
end
local function add_note(text) table.insert(notes_data, text) end
local func
local word_acr = overrides["acr"] and "akronim fonetik" or "sesuatu inisialisme"
if overrides["acr"] or overrides["ini"] then
add_note_number()
if pos ~= "proper" then
if ending_A then
add_note(
"Perkataan ini merupakan " .. word_acr .. " yang berakhir pada " .. mw.getCurrentFrame():expandTemplate{title = 'IPAchar', args = {"/a/"}} .. ". Dalam bentuk jamak " ..
mw.getCurrentFrame():expandTemplate{title = 'angbr', args = {"A"}} .. " dikekalkan dalam teks dan ucapan.")
else
if overrides["acr"] then
add_note("Perkataan ini ialah akronim fonetik. Deklesinya teratur.")
else
if ending_JKXZ then
add_note("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
add_note("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
add_note("This is " .. word_acr .. ". The declension is regular")
end
else
if rfind(noun, "[aeiou]h$") or rfind(noun, "[wy]$") or overrides["ophyph"] or overrides["orthv"] or overrides["orthc"] then
add_note("Optionally, case suffixes can be separated from the root with a hyphen.")
add_note_number()
end
if overrides["orthv"] then
add_note("Words ending in a written vowel but pronounced with a final consonant follow consonant declension in speech but vowel declension in writing.")
add_note_number()
end
if overrides["orthc"] then
add_note("Words ending in a written consonant but pronounced with a final vowel follow vowel declension in speech but consonant declension in writing.")
add_note_number()
end
if rfind(noun, "ah$") and pos ~= "proper" then
add_note("The article is added separately in the singular and the " .. mw.getCurrentFrame():expandTemplate{title = 'angbr', args = {"-ah"}} .. " is kept in the plural.")
add_note_number()
end
if pos == "num" and (noun == "hiru" or noun == "lau") then
add_note("Plural forms in the first column are used when the numeral functions as a noun, while those in the second are used when it functions as a pronoun.")
add_note_number()
end
end
--Note about hamaseiz/hamaseiez
if pos == "num" and rfind(noun, "sei") and noun ~= "sei" then
add_note("The instrumental indefinite of this numeral hasn't been standardized. The ending " ..
mw.getCurrentFrame():expandTemplate{title = 'm', args = {"eu", "", "-eiz"}} .. " can also be found.")
--add_note_number() the superscript number should go in the form itself
end
data.info = data.info .. ")</small>"
--Epenthetic e and r
local e, r, a_s = "e", "r", "a"
if rfind(noun, "[aeiouwyAEIOUWY]h?$") or overrides["ini"] 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["absv|indef"] = {noun}
if rfind(noun, "t[szx]$") then
data.forms["pro|indef"] = {(rsub(noun, "t([szx])$", "%1tzat"))}
else
data.forms["pro|indef"] = {noun .. "tzat"}
end
--double r if needed
if rfind(noun, "r$") and not (overrides["tap"] or overrides["hyph"]) then
noun = noun .. "r"
end
--nouns in -a
local base_def = rfind(noun, "a$") and rsub(noun, "a$", "") or noun
--nouns with compulsory hyphen
if overrides["hyph"] then
noun = noun .. "-"
end
--generate the forms
function get_forms(def_stem)
inflected = {}
--absolutive
inflected["absv|s"] = {def_stem .. a_s}
inflected["absv|p"] = {def_stem .. a_s .. "k"}
inflected["absv|prox|p"] = {def_stem .. "ok"}
--ergative
inflected["erg|indef"] = {noun .. e .. "k"}
inflected["erg|s"] = {def_stem .. a_s .. "k"}
inflected["erg|p"] = {def_stem .. "ek"}
inflected["erg|prox|p"] = {def_stem .. "ok"}
--dative
inflected["dat|indef"] = {noun .. r .. "i"}
inflected["dat|s"] = {def_stem .. a_s .. "ri"}
inflected["dat|p"] = {def_stem .. "ei"}
inflected["dat|prox|p"] = {def_stem .. "oi"}
--comitative
inflected["com|indef"] = {noun .. r .. "ekin"}
inflected["com|s"] = {def_stem .. a_s .. "rekin"}
inflected["com|p"] = {def_stem .. "ekin"}
inflected["com|prox|p"] = {def_stem .. "okin"}
--instrumental
inflected["ins|indef"] = {noun .. e .. e_dip_ins .. "z"}
inflected["ins|s"] = {def_stem .. a_s .. "z"}
inflected["ins|p"] = {def_stem .. "ez"}
inflected["ins|prox|p"] = {def_stem .. "otaz"}
--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[case]) and "|animate" or ""
inflected[case .. an_flag .. "|indef"] = {noun .. r .. "en" .. suf}
inflected[case .. an_flag .. "|s"] = {def_stem .. a_s .. "ren" .. suf}
inflected[case .. an_flag .. "|p"] = {def_stem .. "en" .. suf}
inflected[case .. an_flag .. "|prox|p"] = {def_stem .. "on" .. 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, "[aA]$")) and "a" or ""
inflected[case .. in_flag .. "|indef"] = {noun .. e .. ta .. suf}
inflected[case .. in_flag .. "|s"] = {noun .. e .. e_gau .. a .. suf}
inflected[case .. in_flag .. "|p"] = {def_stem .. "eta" .. suf}
inflected[case .. in_flag .. "|prox|p"] = {def_stem .. "ota" .. suf}
-- Place nouns ending in certain consonants can have forms with and without -e-
if overrides["topo"] and altform_cases[case] 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, {"([nl])ko$", "([nl])tik$", "tzko", "tztik"}, {"%1go", "%1dik", "zko", "ztik"})
if cases_e_last[case] then
inflected[case .. in_flag .. "|indef"] = {form_wo_e, form_w_e}
elseif rfind(noun, "r$") then --only in words like Eibar, Elgoibar...
inflected[case .. in_flag .. "|indef"] = {form_w_e, form_wo_e}
end
end
end
end
--partitive
inflected["par|indef"] = {noun .. r .. "ik"}
return inflected
end
for k, v in pairs(get_forms(base_def)) do data.forms[k] = v end
--extra plural forms (numerals [[hiru]] and [[lau]])
if (noun == "hiru" or noun == "lau") and pos == "num" then
local extra_forms = get_forms(noun .. "r")
for k, v in pairs(extra_forms) do
if rfind(k, "|p") then
for _, val in ipairs(v) do
table.insert(data.forms[k], val)
end
end
end
end
--generate notes
data.notes = next(notes_data) and "<small>" .. m_com.generate_notes_text(notes_data) .. "</small>" or nil
return data
end
----------------------------------------------
----------------------------------------------
local function get_overrides(s, pos)
if type(s) ~= "string" then
return {}
end
--List the allowed indicators
local allowed = m_table.listToSet({"in", "an", "both", "pl", "sg", "pr", "topo", "gau", "md", "tap", "orthv", "orthc", "ini", "acr", "ophyph"})
if pos == "adj" then
allowed = m_table.listToSet({"md", "tap", "orthv", "orthc", "ophyph"})
elseif pos == "num" then
allowed = m_table.listToSet({"md"})
elseif pos == "det" then
allowed = m_table.listToSet({"pl", "md", "tap"})
end
--Split the input and check for duplicates and unallowed indicators
local parts, seen = rsplit(s, "%."), {}
for _, part in ipairs(parts) do
if seen[part] then
error("Duplicate property: '" .. part .. "'.")
end
if not (allowed[part] or rfind(part, "^b:")) then
error("The property '" .. part .. "' isn't supported.")
end
seen[part] = 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[part] 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.main(frame)
local args = frame:getParent().args
-- add the lemma form
local base = args.pagename or mw.title.getCurrentTitle().text
--overrides
local overrides = args[1] or nil
local pos_hardcoded = frame.args.pos or nil
local ort = get_overrides(overrides, pos_hardcoded)
local pos = ort["pr"] and "proper" or nil
if pos_hardcoded and pos then
error("Can't specify 'pr' when the part of speech is an adjective or a numeral.")
end
pos = pos or pos_hardcoded
--animacy (it can be hardcoded in the case of adjectives)
local anim = ((pos == "num" or pos == "adj" or pos == "det") and "both") or get_animacy(ort)
--check for incompatible overrides
local incompat_pairs = {{"ini", "acr"}, {"orthv", "orthc"}, {"sg", "pl"}, {"md", "gau"}, {"tap", "md"}, {"tap", "gau"}, {"ophyph", "orthv"}, {"ophyph", "orthc"}}
for _, p in ipairs(incompat_pairs) do
if ort[p[1]] and ort[p[2]] then
error(("Can't specify '%s' and '%s' at the same time."):format(p[1], p[2]))
end
end
if ort["tap"] and not rfind(base, "r$") then
error("Can't specify 'tap' if the word doesn't end in -r.")
elseif (rfind(base, "[aeiou]h$") or rfind(base, "[wy]$")) and ort["ophyph"] then
error("The paramter 'ophyph' is not needed in words ending in -Vh, -y or -w, the note is added automatically.")
elseif ort["topo"] and (pos ~= "proper" or anim == "an") then
error("The paramter 'topo' can only be applied to inanimate proper nouns.")
elseif ort["topo"] and not rfind(base, "[nlrsz]$") then
error("Can't specify 'topo' to words not ending in -n, -l, -r, -s, -(t)z.")
elseif ort["orthv"] and not rfind(base, "[aeiou]$") then
error("The paramter 'orthv' can only be applied to words ending in an orthographic vowel (e.g. byte).")
elseif ort["orthc"] and rfind(base, "[aeiou]$") then
error("The paramter 'orthc' can only be applied to words ending in an orthographic consonant (e.g. Calais).")
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["sg"] or ort["pl"] 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 rfind(alt_base, "r$") and not ort["tap"] then
alt_base = alt_base .. "r"
end
if not rfind(alt_base, "a$") and (alt_base .. "a" == base) then
ort["sg-def"] = true
elseif not rfind(alt_base, "a$") and (alt_base .. "ak" == base) then
ort["pl-def"] = 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. Dominikar Errepublika
ort["sg-def"] = true
elseif rfind(alt_base, "a$") and (alt_base .. "k" == base) then
ort["pl-def"] = true
else
error("The definite form '" .. base .. "' doesn't match with the indefinite form '" .. alt_base .. "'.")
end
base = rsub(alt_base, "rr$", "r") --restore single -r
end
end
--Handle acronyms
local upper_case = "ABCÇDEFGHIJKLMNÑOPQRSTUVWXYZ"
local is_acronym = rfind(base, "[" .. upper_case .. "]$")
if is_acronym and not (ort["ini"] or ort["acr"]) then
error("This word is either an acronym or an initialism but the required parameters were not specified.")
elseif not is_acronym and (ort["ini"] or ort["acr"]) then -- Mixed-case acronyms get a hyphen
ort["hyph"] = 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" or (pos == "det" and not ort["pl"]) then
table_type = "ind"
end
if anim == "both" then
table_type = table_type .. "-both"
end
if ort["pl"] or ort["pl-def"] then
table_type = table_type .. "-pl"
elseif ort["sg"] or ort["sg-def"] then
table_type = table_type .. "-sg"
end
--Make the table
return make_table(data, table_type)
end
return export