Module:la-headword
Itsura
- This module lacks a documentation subpage. Please create it.
- Useful links: subpage list • links • transclusions • testcases • sandbox
-- TODO: handle "-old" subtypes generally, appending "in Old Latin", where terms have additional OL inflections.
local export = {}
local pos_functions = {}
local string_utilities = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local concat = table.concat
local form_is_empty = require("Module:la-utilities").form_is_empty
local insert = table.insert
local remove = table.remove
local umatch = mw.ustring.match
local lang = require("Module:languages").getByCode("la")
local NAMESPACE = mw.title.getCurrentTitle().nsText
local PAGENAME = mw.loadData("Module:headword/data").pagename
local legal_gender = {
["m"] = true,
["f"] = true,
["n"] = true,
["?"] = true,
["?!"] = true,
}
local declension_to_english = {
["1"] = "first",
["2"] = "second",
["3"] = "third",
["4"] = "fourth",
["5"] = "fifth",
}
local gender_names = {
["m"] = "masculine",
["f"] = "feminine",
["n"] = "neuter",
["?"] = "unknown gender",
["?!"] = "unattested gender",
}
local function deep_equals(...)
deep_equals = require(table_module).deepEquals
return deep_equals(...)
end
local function get_plaintext(...)
get_plaintext = require(utilities_module).get_plaintext
return get_plaintext(...)
end
local function insert_if_not(...)
insert_if_not = require(table_module).insertIfNot
return insert_if_not(...)
end
local function invert(...)
invert = require(table_module).invert
return invert(...)
end
local function keys_to_list(...)
keys_to_list = require(table_module).keysToList
return keys_to_list(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities).pattern_escape
return pattern_escape(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function split(...)
split = require(string_utilities).split
return split(...)
end
local function ulower(...)
ulower = require(string_utilities).lower
return ulower(...)
end
local function format(array, concatenater)
if #array == 0 then
return ""
end
local concatenated = concat(array, concatenater)
if concatenated == "" then
return ""
elseif concatenated:sub(-1) == "'" then
concatenated = concatenated .. " "
end
return "; ''" .. concatenated .. "''"
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[Appendix:Glossary#" .. anchor .. "|" .. text .. "]]"
end
local function make_link(page, display, face, accel)
return require("Module:links").full_link({term = page, alt = display, lang = lang, accel = accel}, face)
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local iargs = require("Module:parameters").process(frame.args, {
[1] = {required = true},
["def"] = true,
["suff_type"] = true,
})
local args = frame:getParent().args
local poscat = iargs[1]
local def = iargs.def
local suff_type = iargs.suff_type
local postype = nil
if suff_type then
postype = poscat .. '-' .. suff_type
else
postype = poscat
end
local data = {lang = lang, categories = {}, heads = {}, genders = {}, inflections = {}}
local infl_classes = {}
local title = {}
local postscript = {}
if poscat == "suffixes" then
insert_if_not(data.categories, "Latin " .. suff_type .. "-forming suffixes")
end
if pos_functions[postype] then
local new_poscat = pos_functions[postype](def, args, data, infl_classes, title, postscript)
if new_poscat then
poscat = new_poscat
end
end
data.pos_category = poscat
postscript = concat(postscript, ", ")
return
require("Module:headword").full_headword(data)
.. format(infl_classes, "/")
.. format(title, ", ")
.. (postscript ~= "" and " (" .. postscript .. ")" or "")
end
local function process_num_type(numtype, categories)
if numtype == "card" then
insert_if_not(categories, "Latin cardinal numbers")
elseif numtype == "ord" then
insert_if_not(categories, "Latin ordinal numbers")
elseif numtype == "frac" then
insert_if_not(categories, "Latin fractional numbers")
elseif numtype == "dist" then
insert_if_not(categories, "Latin distributive numbers")
elseif numtype == "mul" then
insert_if_not(categories, "Latin multiplicative numbers")
elseif numtype == "coll" then
insert_if_not(categories, "Latin collective numbers")
elseif numtype then
error("Unrecognized numeral type '" .. numtype .. "'")
end
end
local function normalize_derivation(deriv, anchor, text)
if not deriv then
return nil
end
local deriv1 = deriv[1]
if not deriv1 or deriv1 == "-" then
return deriv1
end
deriv.label = glossary_link(anchor, text)
return deriv
end
local function derivation(data, decldata, arg, anchor, text)
local der = normalize_derivation(decldata[arg], anchor, text)
if der and der ~= "-" then
insert(data.inflections, der)
end
end
local function nouns(pos, def, args, data, infl_classes, title)
local is_num = pos == "numerals"
local is_pn = false
if pos == "proper nouns" then
is_pn = true
pos = "nouns"
end
local decldata = require("Module:la-nominal").do_generate_noun_forms(args, pos, "headword", def, is_num)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_noun_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local genders = decldata.overriding_genders
if #genders == 0 then
if decldata.gender then
genders = {ulower(decldata.gender)}
elseif not is_num then
error("No gender explicitly specified in headword template using g=, and can't infer gender from lemma spec")
end
end
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("indeclinable")})
insert_if_not(categories, "Latin indeclinable " .. decldata.pos)
for _, g in ipairs(genders) do
local gender = g:match("^(.[\128-\191]*)%-[sp]$")
if not gender then
gender = g
end
if not legal_gender[gender] then
error("Gender “" .. gender .. "” is not a valid Latin gender.")
end
insert(data.genders, g)
insert_if_not(categories, "Latin " .. gender_names[gender] .. " indeclinable " .. decldata.pos)
end
else
local is_irreg = false
local is_indecl = false
local is_decl = false
local has_multiple_decls = false
local has_multiple_variants = false
-- flatten declension specs
local decls = {}
for _, g in ipairs(genders) do
if not legal_gender[g] then
error("Gender “" .. g .. "” is not a valid Latin gender.")
elseif decldata.num == "pl" then
g = g .. "-p"
elseif decldata.num == "sg" then
g = g .. "-s"
end
insert(data.genders, g)
end
local function process_decl(decl_list, props)
local headword_decl = props.headword_decl
-- skip adjectival declensions
if headword_decl:match("+$") then
return
elseif props.decl == "irreg" then
is_irreg = true
headword_decl = headword_decl:match("^irreg/(.*)$") or headword_decl
local irreg_decls = split(headword_decl, ",")
if #irreg_decls > 1 then
has_multiple_decls = true
end
for _, d in ipairs(irreg_decls) do
if d == "indecl" or d == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, d)
end
else
if headword_decl == "indecl" or headword_decl == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, headword_decl)
end
end
for _, props in ipairs(decldata.propses) do
if props.headword_decl then
process_decl(decls, props)
else
local alternant_decls = {}
for _, alternant in ipairs(props) do
for _, single_props in ipairs(alternant) do
process_decl(alternant_decls, single_props)
end
end
if #alternant_decls > 1 then
has_multiple_decls = true
elseif #decls > 1 then
has_multiple_variants = true
end
for _, d in ipairs(alternant_decls) do
insert_if_not(decls, d)
end
end
end
if is_indecl and is_decl then
has_multiple_decls = true
end
if has_multiple_decls then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple declensions")
end
if has_multiple_variants then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple variants of a single declension")
end
if is_irreg then
insert(title, glossary_link("irregular"))
insert_if_not(categories, "Latin irregular " .. decldata.pos)
for _, g in ipairs(genders) do
insert_if_not(categories, "Latin " .. gender_names[g] .. " irregular " .. decldata.pos)
end
end
if is_indecl then
if is_decl then
insert(title, glossary_link("indeclinable"))
else
insert(data.inflections, {label = glossary_link("indeclinable")})
end
insert_if_not(categories, "Latin indeclinable " .. decldata.pos)
for _, g in ipairs(genders) do
insert_if_not(categories, "Latin " .. gender_names[g] .. " indeclinable " .. decldata.pos)
end
end
if #decls > 1 then
insert(title, "variously declined")
end
for _, decl in ipairs(decls) do
if not (decl == "0" or decl == "indecl" or decl == "sgpl" or decl == "irreg") then
local decl_class = declension_to_english[decl]
if not decl_class then
error("Internal error: declension '" .. decl .. "' not recognized")
end
insert(title, "[[Appendix:Latin " .. decl_class .. " declension|" .. decl_class .. " declension]]")
insert_if_not(categories, "Latin " .. decl_class .. " declension " .. decldata.pos)
for _, g in ipairs(genders) do
insert_if_not(categories, "Latin " .. gender_names[g] .. " " .. decldata.pos .. " in the " .. decl_class .. " declension")
end
end
end
local lemma_num = decldata.num == "pl" and "pl" or "sg"
if NAMESPACE == "Reconstruction" then
-- For reconstructed nouns:
if data.genders[1] == 'n' and lemma_num == 'sg' then
-- singular neuter nouns give a plural
local pl = decldata.forms["nom_pl"]
if pl and pl ~= "" and #pl > 0 then
pl.label = "plural"
insert(data.inflections, pl)
end
else
-- all others give an oblique
local obl = decldata.forms["acc_" .. lemma_num]
if obl and obl ~= "" and #obl > 0 then
obl.label = "oblique"
insert(data.inflections, obl)
end
end
else
local gen = decldata.forms["gen_" .. lemma_num]
if (decldata.unattested["gen_" .. lemma_num]) then
gen[1] = '*' .. gen[1]
data.nolink = true
end
if gen and gen ~= "" and gen ~= "—" and #gen > 0 then
if is_decl then
-- Skip displaying the genitive for nouns that are only
-- indeclinable. But we do display it for nouns like Abrahām
-- and Ādām that can be either indeclinable or declined.
gen.label = "genitive"
insert(data.inflections, gen)
end
else
insert(data.inflections, {label = "no genitive"})
insert_if_not(categories, "Latin " .. decldata.pos .. " with no genitive singular")
end
end
end
derivation(data, decldata, "m", "masculine")
derivation(data, decldata, "f", "feminine")
derivation(data, decldata, "n", "neuter")
derivation(data, decldata, "adj", "relational", "relational adjective")
derivation(data, decldata, "dim", "diminutive")
derivation(data, decldata, "aug", "augmentative")
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, "Latin " .. cat)
end
return is_pn and decldata.pos == "nouns" and "proper nouns" or decldata.pos
end
pos_functions["nouns"] = function(def, args, data, infl_classes, title)
return nouns("nouns", def, args, data, infl_classes, title)
end
pos_functions["proper nouns"] = function(def, args, data, infl_classes, title)
return nouns("proper nouns", def, args, data, infl_classes, title)
end
pos_functions["suffixes-noun"] = function(def, args, data, infl_classes, title)
return nouns("suffixes", def, args, data, infl_classes, title)
end
pos_functions["numerals-noun"] = function(def, args, data, infl_classes, title)
return nouns("numerals", def, args, data, infl_classes, title)
end
function export.verb_title(title, typeinfo, lemma_forms)
local conj = typeinfo.conj
local irreg_processing = typeinfo.irreg
local subtypes = typeinfo.subtypes
local first_lemma = ""
if #lemma_forms > 0 then
first_lemma = require("Module:links").remove_links(lemma_forms[1])
end
if conj == "1st" then
insert(title, "[[Appendix:Latin first conjugation|first conjugation]]")
elseif conj == "2nd" then
insert(title, "[[Appendix:Latin second conjugation|second conjugation]]")
elseif conj == "3rd" then
insert(title, "[[Appendix:Latin third conjugation|third conjugation]]")
elseif conj == "3rd-io" then
insert(title, ("[[Appendix:Latin third conjugation|third (%s variant) conjugation]]"):format(make_link(nil, "-iō", "term")))
elseif conj == "3rd/4th" then
insert(title, ("[[Appendix:Latin third conjugation|third (%s variant)]] / [[Appendix:Latin fourth conjugation|fourth conjugation]]"):format(make_link(nil, "-iō", "term")))
elseif conj == "4th" then
insert(title, "[[Appendix:Latin fourth conjugation|fourth conjugation]]")
elseif conj == "irreg" then -- sum
insert(title, "[[Appendix:Latin irregular verbs|irregular conjugation]]")
end
if subtypes.highlydef then -- āiō, inquam
insert(title, "highly [[defective verb#English|defective]]")
end
if subtypes.suppl then -- sum, volō
insert(title, "[[suppletive#English|suppletive]]")
end
if subtypes["3only"] then -- decet
insert(title, "[[third person#English|third person]]-only")
elseif subtypes.impers then -- decet, advesperāscit (also nopass)
insert(title, "[[impersonal verb#English|impersonal]]")
end
if subtypes.depon then -- dēmōlior, calvor (also noperf)
insert(title, "[[deponent#English|deponent]]")
end
if subtypes.perfaspres then -- meminī, ōdī
insert(title, "no [[present tense#English|present]] stem")
-- If semidepon is set, only the active forms are affected.
local voice = subtypes.semidepon and "[[active voice#English|active]] " or ""
insert(title, ("[[perfect tense#English|perfect]] %sforms have [[present tense#English|present]] %smeaning"):format(voice, voice))
-- Clarify that deponency can only apply to the perfect.
if subtypes.semidepon then -- ōdī
insert(title, "[[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
if subtypes.optsemidepon then
insert(title, "optionally [[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
else
if subtypes.semidepon then -- fīdō, gaudeō
insert(title, "[[semi-deponent#English|semi-deponent]]")
end
if subtypes.optsemidepon then -- audeō, placeō, soleō, pudeō
insert(title, "optionally [[semi-deponent#English|semi-deponent]]")
end
end
if subtypes.imponly then -- cedo, apage
insert(title, "[[imperative mood#English|imperative]]-only")
end
if subtypes.nopass then -- coacēscō
insert(title, "no [[passive voice#English|passive]]")
end
local stems = {}
if subtypes.nopres then -- coepī; perfaspres already handles this above
insert(stems, "[[present tense#English|present]]")
end
if subtypes.noperf then
insert(stems, "[[perfect tense#English|perfect]]")
end
if subtypes.nosup or subtypes.supfutractvonly then
insert(stems, "[[supine#English|supine]]")
end
if #stems > 0 then
local extra = subtypes.supfutractvonly and " except in the [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]"
insert(title, ("no %s stem%s%s"):format(serial_comma_join(stems, {conj = "or"}), #stems > 1 and "s" or "", extra or ""))
end
if subtypes.nofutr then -- soleō
insert(title, "no [[future tense#English|future]]")
end
if subtypes.pass3only then -- praefundō
insert(title, "[[third person#English|third person]]-only in the [[passive voice#English|passive]]")
elseif subtypes.passimpers then -- abambulō
local msg = "[[impersonal verb#English|impersonal]] in the [[passive voice#English|passive]]"
if subtypes.passimpersold then -- possum
msg = msg .. " in [[Old Latin]]"
end
insert(title, msg)
end
if subtypes.noimp then -- volō
insert(title, "no [[imperative mood#English|imperative]]")
end
if irreg_processing and umatch(first_lemma, "d[īū]cō$") then -- dīcō
insert(title, "[[Appendix:Latin irregular verbs|irregular]] short [[imperative mood#English|imperative]]")
end
if subtypes.nofutractvptc and not subtypes.nosup then -- fīō
insert(title, "no [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]")
end
if not (subtypes.nopres or subtypes.perfaspres or subtypes.imponly) then
if subtypes.noinf then -- inquam
insert(title, "no [[infinitive#English|infinitive]]")
end
if subtypes.noger then -- libet
insert(title, "no [[gerund#English|gerund]]")
end
end
if subtypes.shorta then -- dō
insert(title, ("[[Appendix:Latin irregular verbs|irregular]] short %s in most forms"):format(make_link(nil, "ă", "term")))
end
if irreg_processing then
if first_lemma:match("edō$") then -- edō
insert(title, "[[Appendix:Latin irregular verbs|irregular]] alternative forms")
elseif first_lemma:match("fīō$") then -- fīō
insert(title, "[[Appendix:Latin irregular verbs|irregular]] long " .. make_link(nil, "ī", "term"))
end
end
end
pos_functions["verbs"] = function(def, args, data, infl_classes, title)
local m_la_verb = require("Module:la-verb")
local def1, def2
if def then
def1, def2 = def:match("^(.-):(.*)$")
end
local conjdata, typeinfo = m_la_verb.make_data(args, true, def1, def2)
local lemma_forms = conjdata.overriding_lemma
if not lemma_forms or #lemma_forms == 0 then
lemma_forms = m_la_verb.get_lemma_forms(conjdata, true)
end
data.heads = lemma_forms
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-verb}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = conjdata.id
local perf_only = false
local function insert_inflection(infl, label)
infl.label = label
insert(data.inflections, infl)
end
local inf = m_la_verb.get_valid_forms(conjdata.forms["pres_actv_inf"])
if #inf > 0 then
insert_inflection(inf, "present infinitive")
else
inf = m_la_verb.get_valid_forms(conjdata.forms["perf_actv_inf"])
if #inf > 0 then
perf_only = true
insert_inflection(inf, "perfect infinitive")
end
end
if not perf_only then
local perf = m_la_verb.get_valid_forms(conjdata.forms["1s_perf_actv_indc"])
if #perf == 0 then
perf = m_la_verb.get_valid_forms(conjdata.forms["3s_perf_actv_indc"])
end
if #perf > 0 then
insert_inflection(perf, "perfect active")
end
end
local subtypes = typeinfo.subtypes
if not (subtypes.depon or subtypes.semidepon) then
local sup = m_la_verb.get_valid_forms(conjdata.forms["acc_sup"])
if #sup > 0 then
insert_inflection(sup, "supine")
else
local fap = m_la_verb.get_valid_forms(conjdata.forms["futr_actv_ptc"])
if #fap > 0 then
insert_inflection(fap, "future active participle")
end
end
end
export.verb_title(title, typeinfo, lemma_forms)
end
pos_functions["suffixes-verb"] = pos_functions["verbs"]
local function attested_form(decldata, index)
local form
if (decldata.unattested[index]) then
form = { { term = '*' .. decldata.forms[index][1], nolink = true } }
else
form = decldata.forms[index]
end
return form
end
local function degree_derivations(comp, sup, data)
local inflections = data.inflections
if not (comp or sup) then
return
elseif (not comp or comp == "-") and (not sup or sup == "-") then
insert(inflections, {label = "not [[Appendix:Glossary#comparative|comparable]]"})
insert_if_not(data.categories, "Latin uncomparable adverbs")
return
elseif comp == "-" then
insert(inflections, {label = "no [[Appendix:Glossary#comparative|comparative]]"})
elseif comp then
insert(inflections, comp)
end
if sup == "-" then
insert(inflections, {label = "no [[Appendix:Glossary#superlative|superlative]]"})
elseif sup then
insert(inflections, sup)
end
end
local function adjectives(pos, def, args, data, infl_classes)
local is_num = pos == "numerals"
local decldata = require("Module:la-nominal").do_generate_adj_forms(args, pos, "headword", nil, def)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_adj_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.num == "pl" then
insert_if_not(categories, "Latin plural-only " .. decldata.pos)
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("indeclinable")})
if decldata.pos == "participles" then
local lemma1 = lemma[1]
if lemma1:sub(-4) == "ndum" then
insert_if_not(categories, "Latin future passive participles")
elseif lemma1:match("um$") then
insert_if_not(categories, "Latin perfect participles")
end
end
else
local lemma_num = decldata.num == "pl" and "pl" or "sg"
local masc = decldata.forms["nom_" .. lemma_num .. "_m"]
local fem = attested_form(decldata, "nom_" .. lemma_num .. "_f")
local neut = attested_form(decldata, "nom_" .. lemma_num .. "_n")
local gen = attested_form(decldata, "gen_" .. lemma_num .. "_m")
local acc = attested_form(decldata, "acc_" .. lemma_num .. "_m")
if decldata.pos == "participles" then
local masc1 = masc[1]
if masc1:sub(-5) == "ūrus" then
insert_if_not(categories, "Latin future active participles")
elseif masc1:sub(-4) == "ndus" then
-- FIXME, should rename to "Latin gerundives")
insert_if_not(categories, "Latin future passive participles")
else
local masc1_final2 = masc1:sub(-2)
if masc1_final2 == "us" then
insert_if_not(categories, "Latin perfect participles")
elseif masc1_final2 == "ns" then
insert_if_not(categories, "Latin present participles")
else
error("Unrecognized participle ending: " .. masc1)
end
end
end
-- We display the inflections in three different ways to mimic the
-- old way of doing things:
--
-- 1. If masc and fem are different, show masc, fem and neut.
-- 2. Otherwise, if masc and neut are different, show masc and neut.
-- 3. Otherwise, show masc nominative and masc genitive.
if not form_is_empty(fem) and not deep_equals(masc, fem) then
fem.label = "feminine"
insert(data.inflections, fem)
if not form_is_empty(neut) then
neut.label = "neuter"
insert(data.inflections, neut)
end
elseif not form_is_empty(neut) and not deep_equals(masc, neut) then
neut.label = "neuter"
insert(data.inflections, neut)
elseif not form_is_empty(gen) then
gen.label = "genitive"
insert(data.inflections, gen)
elseif not form_is_empty(acc) then
acc.label = "accusative"
insert(data.inflections, acc)
end
insert(infl_classes, decldata.title)
end
local comp = normalize_derivation(decldata.comp, "comparative")
local sup = normalize_derivation(decldata.sup, "superlative")
degree_derivations(comp, sup, data)
derivation(data, decldata, "adv", "adverb")
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, "Latin " .. cat)
end
return decldata.pos
end
pos_functions["adjectives"] = function(def, args, data, infl_classes, title)
return adjectives("adjectives", def, args, data, infl_classes, title)
end
pos_functions["participles"] = function(def, args, data, infl_classes, title)
return adjectives("participles", def, args, data, infl_classes, title)
end
pos_functions["determiners"] = function(def, args, data, infl_classes, title)
return adjectives("determiners", def, args, data, infl_classes, title)
end
pos_functions["pronouns"] = function(def, args, data, infl_classes, title)
return adjectives("pronouns", def, args, data, infl_classes, title)
end
pos_functions["suffixes-adjective"] = function(def, args, data, infl_classes, title)
return adjectives("suffixes", def, args, data, infl_classes, title)
end
pos_functions["numerals-adjective"] = function(def, args, data, infl_classes, title)
return adjectives("numerals", def, args, data, infl_classes, title)
end
pos_functions["adverbs"] = function(def, args, data)
local sublist = {sublist = "/"}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {alias_of = "comp"},
[3] = {alias_of = "sup"},
["head"] = {list = true, required = true},
["comp"] = sublist,
["sup"] = sublist,
["adj"] = sublist,
["id"] = true,
})
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
local comp, sup = args.comp, args.sup
local irreg = false
if comp then
if comp[1] == "-" then
comp = "-"
elseif comp[1] == nil then
comp = nil
else
comp.label = glossary_link("comparative")
comp = args.comp
irreg = true
end
end
if sup then
if sup[1] == "-" then
sup = "-"
elseif sup[1] == nil then
sup = nil
else
sup.label = glossary_link("superlative")
sup = args.sup
irreg = true
end
end
local categories = data.categories
if irreg then
insert_if_not(categories, "Latin irregular adverbs")
end
if not (comp or sup) then
local default_comp = {label = glossary_link("comparative")}
local default_sup = {label = glossary_link("superlative")}
for _, head in ipairs(args.head) do
local stem = nil
for _, suff in ipairs{"iter", "nter", "ter", "er", "iē", "ē", "rā", "im", "ō"} do
stem = head:match("(.*)" .. pattern_escape(suff) .. "$")
comp_suff, sup_suff = "ius", "issimē"
if stem ~= nil then
if suff == "nter" then
stem = stem .. "nt"
elseif suff == "rā" then
comp_suff = "er" .. comp_suff
sup_suff = "imē"
end
insert(default_comp, stem .. comp_suff)
insert(default_sup, stem .. sup_suff)
break
end
end
if not stem then
error("Unrecognized adverb type, recognized types are “-ē”, “-er”, “-ter”, “-iter”, “-im”, or “-ō”, “-ra” or specify irregular forms or “-” if incomparable.")
end
end
comp = comp or default_comp
sup = sup or default_sup
end
degree_derivations(comp, sup, data)
derivation(data, args, "adj", "adjective")
end
pos_functions["suffixes-adverb"] = pos_functions["adverbs"]
local function get_forms(forms)
if #forms == 0 then
return nil
end
local i, attested = 1, false
while true do
local form = forms[i]
if form == nil then
return forms, attested
elseif form == "-" then
remove(forms, i)
else
if not (attested or get_plaintext(form):sub(1, 1) == "*") then
attested = true
end
i = i + 1
end
end
end
local function degree(pos, deg, pos_func, other_pos, other_pos_label, other_deg, other_deg_label, args, data, infl_classes)
local list = {list = true}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
["head"] = list,
["positive"] = list,
[other_pos] = {sublist = "/"},
[other_deg] = list,
["id"] = true,
})
data.no_redundant_head_cat = #args.head == 0
-- Set default manually so we can tell whether the user specified head=.
if #args.head == 0 then
args.head = {PAGENAME}
end
data.heads = args.head
data.id = args.id
insert(data.inflections, {label = deg})
if pos_func then
pos_func(args, data, infl_classes)
end
local positive, positive_attested = get_forms(args.positive)
if positive then
if not positive_attested then
insert(data.categories, "Latin " .. deg .. "-only " .. pos)
end
if #positive > 0 then
args.positive.label = "positive"
insert(data.inflections, args.positive)
else
insert(data.inflections, {label = "no positive form"})
end
end
local other = get_forms(args[other_deg])
if other then
if #other > 0 then
args[other_deg].label = other_deg_label
insert(data.inflections, args[other_deg])
else
insert(data.inflections, {label = "no " .. other_deg_label .. " form"})
end
end
derivation(data, args, other_pos, other_pos_label)
-- If a lemma, return the primary part of speech ("adjectives" or
-- "adverbs"), so that the term is categorized in "Latin adjectives" or
-- "Latin adverbs". Otherwise, return nothing, so that the term goes in the
-- relevant non-lemma category (e.g. "Latin comparative adjectives"), and
-- into "Latin non-lemma forms".
if positive and not positive_attested then
return pos
end
end
local function comp_adj(args, data, infl_classes)
insert(infl_classes, "[[Appendix:Latin third declension|third declension]]")
local n = {label = "neuter"}
for _, head in ipairs(args.head) do
local neuter = head:gsub("or$", "us")
insert(n, neuter)
end
insert(data.inflections, n)
end
pos_functions["comparative adjectives"] = function(def, args, data, infl_classes, title)
return degree("adjectives", "comparative", comp_adj, "adv", "adverb", "sup", "superlative", args, data, infl_classes)
end
pos_functions["comparative adverbs"] = function(def, args, data, infl_classes, title)
return degree("adverbs", "comparative", nil, "adj", "adjective", "sup", "superlative", args, data, infl_classes)
end
local function sup_adj(args, data, infl_classes)
insert(infl_classes, "[[Appendix:Latin first declension|first]]")
insert(infl_classes, "[[Appendix:Latin second declension|second declension]]")
local f, n = {label = "feminine"}, {label = "neuter"}
for _, head in ipairs(args.head) do
local stem = head:gsub("us$", "")
insert(f, stem .. "a")
insert(n, stem .. "um")
end
insert(data.inflections, f)
insert(data.inflections, n)
end
pos_functions["superlative adjectives"] = function(def, args, data, infl_classes, title)
return degree("adjectives", "superlative", sup_adj, "adv", "adverb", "comp", "comparative", args, data, infl_classes)
end
pos_functions["superlative adverbs"] = function(def, args, data, infl_classes, title)
return degree("adverbs", "superlative", nil, "adj", "adjective", "comp", "comparative", args, data, infl_classes)
end
local function adpositions(pos, def, args, data, infl_classes, title, postscript)
local cases = invert(require("Module:la-utilities").cases)
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {list = true, set = keys_to_list(cases)},
["head"] = {list = true, required = true},
["id"] = true,
})
-- Case names are supplied in numbered arguments, optionally preceded by
-- headwords.
cases = args[2]
for i = 1, #cases do
for j = i + 1, #cases do
if cases[i] == cases[j] then
error("Duplicate case")
end
end
local case = cases[i]
local appendix_link = glossary_link(case)
if i == 1 then
appendix_link = "+ " .. appendix_link
end
insert(postscript, appendix_link)
insert_if_not(data.categories, "Latin " .. case .. " " .. pos)
end
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
end
pos_functions["prepositions"] = function(...)
return adpositions("prepositions", ...)
end
pos_functions["postpositions"] = function(...)
return adpositions("postpositions", ...)
end
pos_functions["gerunds"] = function(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = "labōrandum"}, -- headword
[2] = true, -- gerundive
})
data.heads = {args[1]}
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
insert(data.inflections, {label = "[[Appendix:Glossary#accusative|accusative]]"})
local stem = args[1]:match("^(.*)um$")
if not stem then
error("Unrecognized gerund ending: " .. stem)
end
if args[2] == "-" then
insert(data.inflections, {label = "no [[Appendix:Glossary#gerundive|gerundive]]"})
else
insert(data.inflections, {[1] = args[2] or stem .. "us", label = "[[Appendix:Glossary#gerundive|gerundive]]"})
end
end
local function non_lemma_forms(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = def}, -- headword or cases
["head"] = {list = true, require_index = true},
["g"] = {list = true},
["id"] = true,
})
local heads = {args[1]}
for _, head in ipairs(args.head) do
insert(heads, head)
end
data.heads = heads
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
data.genders = args.g
data.id = args.id
end
pos_functions["noun forms"] = non_lemma_forms
pos_functions["proper noun forms"] = non_lemma_forms
pos_functions["pronoun forms"] = non_lemma_forms
pos_functions["verb forms"] = non_lemma_forms
pos_functions["gerund forms"] = non_lemma_forms
pos_functions["adjective forms"] = non_lemma_forms
pos_functions["participle forms"] = non_lemma_forms
pos_functions["determiner forms"] = non_lemma_forms
pos_functions["numeral forms"] = non_lemma_forms
pos_functions["suffix forms"] = non_lemma_forms
return export