-- This module contains code for Italian headword templates.
-- Templates covered are:
-- * {{it-noun}}, {{it-proper noun}};
-- * {{it-verb}};
-- * {{it-adj}}, {{it-adj-comp}}, {{it-adj-sup}};
-- * {{it-det}};
-- * {{it-art}};
-- * {{it-pron-adj}};
-- * {{it-pp}};
-- * {{it-presp}};
-- * {{it-card-noun}}, {{it-card-adj}}, {{it-card-inv}};
-- * {{it-adv}};
-- * {{it-pos}};
-- * {{it-suffix form}}.
-- See ] for Italian conjugation templates.
local export = {}
local pos_functions = {}
local force_cat = false -- for testing; if true, categories appear in non-mainspace pages
local m_strutils = require("Module:string utilities")
local usub = m_strutils.sub
local require_when_needed = require("Module:utilities/require when needed")
local insert = table.insert
local m_table = require("Module:table")
local com = require("Module:it-common")
local en_utilities_module = "Module:en-utilities"
local headword_module = "Module:headword"
local headword_utilities_module = "Module:headword utilities"
local inflection_utilities_module = "Module:inflection utilities"
local it_verb_module = "Module:it-verb"
local parse_interface_module = "Module:parse interface"
local romut_module = "Module:romance utilities"
local lang = require("Module:languages").getByCode("it")
local langname = lang:getCanonicalName()
local m_en_utilities = require_when_needed(en_utilities_module)
local m_headword_utilities = require_when_needed(headword_utilities_module)
local glossary_link = require_when_needed(headword_utilities_module, "glossary_link")
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local no_split_apostrophe_words = {
= true,
= true,
= true,
}
-----------------------------------------------------------------------------------------
-- Utility functions --
-----------------------------------------------------------------------------------------
local function track(page)
require("Module:debug/track")("it-headword/" .. page)
return true
end
-- Parse and insert an inflection not requiring additional processing into `data.inflections`. The raw arguments come
-- from `args`, which is parsed for inline modifiers. `label` is the label that the inflections are given;
-- `plpos` is the plural part of speech, used in ].
-- `accel` is the accelerator form, or nil.
local function parse_and_insert_inflection(data, args, field, label, plpos, accel, frob)
m_headword_utilities.parse_and_insert_inflection {
headdata = data,
forms = args,
paramname = field,
label = label,
accel = accel and {form = accel} or nil,
frob = frob,
check_missing = true,
lang = lang,
plpos = plpos,
}
end
local function replace_hash_with_lemma(term, lemma)
-- If there is a % sign in the lemma, we have to replace it with %% so it doesn't get interpreted as a capture
-- replace expression.
lemma = lemma:gsub("%%", "%%%%")
return (term:gsub("#", lemma))
end
local list_param = {list = true, disallow_holes = true}
local boolean_param = {type = "boolean"}
-----------------------------------------------------------------------------------------
-- Main entry point --
-----------------------------------------------------------------------------------------
function export.show(frame)
local poscat = frame.args
or error("Part of speech has not been specified. Please pass parameter 1 to the module invocation.")
local parargs = frame:getParent().args
local params = {
= list_param,
= true,
= true,
= boolean_param,
= boolean_param,
= boolean_param,
= boolean_param,
= true, -- for testing
}
if pos_functions then
for key, val in pairs(pos_functions.params) do
params = val
end
end
local args = require("Module:parameters").process(parargs, params)
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
local user_specified_heads = args.head
local heads = user_specified_heads
if args.nolinkhead then
if #heads == 0 then
heads = {pagename}
end
else
local romut = require(romut_module)
local auto_linked_head = romut.add_links_to_multiword_term(pagename, args.splithyph,
no_split_apostrophe_words)
if #heads == 0 then
heads = {auto_linked_head}
else
for i, head in ipairs(heads) do
if head:find("^~") then
head = romut.apply_link_modifiers(auto_linked_head, usub(head, 2))
heads = head
end
if head == auto_linked_head then
track("redundant-head")
end
end
end
end
local data = {
lang = lang,
pos_category = poscat,
categories = {},
heads = heads,
user_specified_heads = user_specified_heads,
no_redundant_head_cat = #user_specified_heads == 0,
genders = {},
inflections = {},
pagename = pagename,
id = args.id,
sort_key = args.sort,
force_cat_output = force_cat,
}
local is_suffix = false
if pagename:find("^%-") and poscat ~= "suffix forms" then
is_suffix = true
data.pos_category = "suffixes"
local singular_poscat = m_en_utilities.singularize(poscat)
insert(data.categories, langname .. " " .. singular_poscat .. "-forming suffixes")
insert(data.inflections, {label = singular_poscat .. "-forming suffix"})
end
if pos_functions then
pos_functions.func(args, data, is_suffix)
end
if args.apoc then
-- Apocopated form of a term; do this after calling pos_functions, because the function might modify
-- data.pos_category.
local pos = data.pos_category
if not pos:find(" forms") then
-- Apocopated forms are non-lemma forms.
local singular_poscat = m_en_utilities.singularize(pos)
data.pos_category = singular_poscat .. " forms"
end
-- If this is a suffix, insert label 'apocopated' after 'FOO-forming suffix', otherwise insert at the beginning.
insert(data.inflections, is_suffix and 2 or 1, {label = glossary_link("apocopated")})
end
if args.json then
return require("Module:JSON").toJSON(data)
end
return require(headword_module).full_headword(data)
end
local deriv_params = {
{"dim", glossary_link("diminutive")},
{"aug", glossary_link("augmentative")},
{"pej", glossary_link("pejorative")},
{"derog", glossary_link("derogatory")},
{"end", glossary_link("endearing")},
{"dim_aug", glossary_link("diminutive") .. "-" .. glossary_link("augmentative")},
{"dim_pej", glossary_link("diminutive") .. "-" .. glossary_link("pejorative")},
{"dim_derog", glossary_link("diminutive") .. "-" .. glossary_link("derogatory")},
{"dim_end", glossary_link("diminutive") .. "-" .. glossary_link("endearing")},
{"aug_pej", glossary_link("augmentative") .. "-" .. glossary_link("pejorative")},
{"aug_derog", glossary_link("augmentative") .. "-" .. glossary_link("derogatory")},
{"aug_end", glossary_link("augmentative") .. "-" .. glossary_link("endearing")},
{"end_derog", glossary_link("endearing") .. "-" .. glossary_link("derogatory")},
}
local function insert_deriv_params(params)
for _, deriv_param in ipairs(deriv_params) do
local param, desc = unpack(deriv_param)
params = list_param
end
end
local param_mods = {
t = {
-- We need to store the <t:...> inline modifier into the "gloss" key of the parsed part, because that is what
-- ] expects.
item_dest = "gloss",
},
gloss = {},
-- no 'tr' or 'ts', doesn't make sense for Italian
g = {
-- We need to store the <g:...> inline modifier into the "genders" key of the parsed part, because that is what
-- ] expects.
item_dest = "genders",
sublist = true,
},
id = {},
alt = {},
q = {type = "qualifier"},
qq = {type = "qualifier"},
lit = {},
pos = {},
-- no 'sc', doesn't make sense for Italian
}
local function parse_term_with_modifiers(paramname, val)
local function generate_obj(term)
local decomp = com.decompose(term)
local lemma = com.remove_non_final_accents(decomp)
if lemma ~= decomp then
term = com.compose("]")
end
return {term = term}
end
local retval = require(parse_interface_module).parse_inline_modifiers(val, {
paramname = paramname,
param_mods = param_mods,
generate_obj = generate_obj,
splitchar = "",
preserve_splitchar = true,
})
for _, obj in ipairs(retval) do
if obj.delimiter == ";" then
obj.separator = "; "
elseif obj.delimiter == "/" then
obj.separator = "/"
-- default to nil for comma
end
end
return retval
end
local function insert_deriv_inflections(data, args, plpos)
for _, deriv_param in ipairs(deriv_params) do
local param, desc = unpack(deriv_param)
if #args > 0 then
local inflection = {label = desc}
for i, term in ipairs(args) do
local parsed_terms = parse_term_with_modifiers(param, term)
for _, parsed_term in ipairs(parsed_terms) do
insert(inflection, parsed_term)
end
end
-- These will typically be missing for now so it doesn't help to do this.
-- check_all_missing(inflection, plpos, data.categories)
insert(data.inflections, inflection)
end
end
end
-----------------------------------------------------------------------------------------
-- Nouns --
-----------------------------------------------------------------------------------------
local allowed_genders = m_table.listToSet(
{"m", "f", "mf", "mfbysense", "mfequiv", "gneut", "n", "m-p", "f-p", "mf-p", "mfbysense-p", "mfequiv-p", "gneut-p", "n-p", "?", "?-p"}
)
local function validate_genders(genders)
for _, g in ipairs(genders) do
if type(g) == "table" then
g = g.spec
end
if not allowed_genders then
error("Unrecognized gender: " .. g)
end
end
end
local function do_noun(args, data, pos, is_suffix, is_proper)
local is_plurale_tantum = false
local has_singular = false
if is_suffix then
pos = "suffix"
end
local plpos = m_en_utilities.pluralize(pos)
validate_genders(args)
data.genders = args
local saw_m = false
local saw_f = false
local gender_for_default_plural
-- Check for specific genders and pluralia tantum.
for _, g in ipairs(args) do
if type(g) == "table" then
g = g.spec
end
if g:find("-p$") then
is_plurale_tantum = true
else
has_singular = true
if g == "m" or g == "mf" or g == "mfbysense" then
saw_m = true
end
if g == "f" or g == "mf" or g == "mfbysense" then
saw_f = true
end
end
end
if saw_m and saw_f then
gender_for_default_plural = "mf"
elseif saw_f then
gender_for_default_plural = "f"
else
gender_for_default_plural = "m"
end
local lemma = data.pagename
local function inscat(cat)
insert(data.categories, langname .. " " .. cat)
end
local function inslabel(label)
insert(data.inflections, {label = label})
end
local function insert_noun_inflection(terms, label, accel, no_inv)
for _, term in ipairs(terms) do
term.term_for_further_inflection = term
if not no_inv and term.term == lemma then
term.term = nil
term.label = glossary_link("invariable")
end
end
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
check_missing = true,
lang = lang,
plpos = plpos,
}
end
-- Plural
local plurals = {}
-- Fetch explicit masculine and feminine plurals here because we may change them below when processing plurals.
local mpls = m_headword_utilities.parse_term_list_with_modifiers {
paramname = "mpl",
forms = args.mpl,
}
local fpls = m_headword_utilities.parse_term_list_with_modifiers {
paramname = "fpl",
forms = args.fpl,
}
if is_plurale_tantum and not has_singular then
if args then
error("Can't specify plurals of plurale tantum " .. pos)
end
insert(data.inflections, {label = glossary_link("plural only")})
elseif args.apoc then
-- apocopated noun
if args then
error("Can't specify plurals of apocopated " .. pos)
end
else
-- Fetch plurals and associated qualifiers, labels and genders.
plurals = m_headword_utilities.parse_term_list_with_modifiers {
paramname = {2, "pl"},
forms = args,
include_mods = {"g"},
}
-- Check for special plural signals
local mode = nil
local pl1 = plurals
if pl1 and #pl1.term == 1 then
mode = pl1.term
if mode == "?" or mode == "!" or mode == "-" or mode == "~" then
pl1.term = nil
if next(pl1) then
error(("Can't specify inline modifiers with plural code '%s'"):format(mode))
end
table.remove(plurals, 1) -- Remove the mode parameter
elseif mode ~= "+" and mode ~= "#" then
error(("Unexpected plural code '%s'"):format(mode))
end
end
if is_plurale_tantum then
-- both singular and plural
insert(data.inflections, {label = "sometimes " .. glossary_link("plural only") .. ", in variation"})
end
if mode == "?" then
-- Plural is unknown
insert(data.categories, langname .. " " .. plpos .. " with unknown or uncertain plurals")
elseif mode == "!" then
-- Plural is not attested
insert(data.inflections, {label = "plural not attested"})
insert(data.categories, langname .. " " .. plpos .. " with unattested plurals")
if plurals then
error("Can't specify any plurals along with unattested plural code '!'")
end
elseif mode == "-" then
-- Uncountable noun; may occasionally have a plural
insert(data.categories, langname .. " uncountable " .. plpos)
-- If plural forms were given explicitly, then show "usually"
if plurals then
insert(data.inflections, {label = "usually " .. glossary_link("uncountable")})
insert(data.categories, langname .. " countable " .. plpos)
else
insert(data.inflections, {label = glossary_link("uncountable")})
end
else
-- Countable or mixed countable/uncountable
-- If no plurals, use the default plural unless mpl= or fpl= explicitly given.
if not plurals and not mpls and not fpls and not is_proper then
plurals = {term = "+"}
end
if mode == "~" then
-- Mixed countable/uncountable noun, always has a plural
insert(data.inflections, {label = glossary_link("countable") .. " and " .. glossary_link("uncountable")})
insert(data.categories, langname .. " uncountable " .. plpos)
insert(data.categories, langname .. " countable " .. plpos)
elseif plurals then
-- Countable nouns
insert(data.categories, langname .. " countable " .. plpos)
else
-- Uncountable nouns
insert(data.categories, langname .. " uncountable " .. plpos)
end
end
-- Process plurals, handling requests for default plurals.
local has_default_or_hash = false
for _, pl in ipairs(plurals) do
if pl.term:find("^%+") or pl.term:find("#") or pl.term == "cap*" or pl.term == "cap*+" then
has_default_or_hash = true
break
end
end
if has_default_or_hash then
local newpls = {}
local function insert_pl(pl, defpl)
pl.term = defpl
insert(newpls, pl)
end
local function make_gendered_plural(pl, special)
if gender_for_default_plural == "mf" then
local default_mpl = com.make_plural(lemma, "m", special)
local default_fpl = com.make_plural(lemma, "f", special)
if default_mpl then
if default_mpl == default_fpl then
insert_pl(pl, default_mpl)
else
if args.mpl or args.fpl then
error("Can't specify gendered plural spec '" .. (special or "+") ..
"' along with gender=" .. gender_for_default_plural ..
" and also specify mpl= or fpl=")
end
mpls = {m_table.shallowCopy(pl)}
mpls.term = default_mpl
fpls = {pl}
fpls.term = default_fpl
end
end
else
local defpl = com.make_plural(lemma, gender_for_default_plural, special)
if defpl then
insert_pl(pl, defpl)
end
end
end
for _, pl in ipairs(plurals) do
if pl.term == "cap*" or pl.term == "cap*+" then
make_gendered_plural(pl, pl.term)
elseif pl.term == "+" then
make_gendered_plural(pl)
elseif pl.term:find("^%+") then
local special = require(romut_module).get_special_indicator(pl.term)
make_gendered_plural(pl, special)
else
insert_pl(pl, replace_hash_with_lemma(pl.term, lemma))
end
end
plurals = newpls
end
if plurals then
inscat(plpos .. " with multiple plurals")
end
-- If the first or only plural is the same as the singular, replace it with 'invariable', or 'usually
-- invariable' if there is more than one plural.
local pl1 = plurals
if pl1 and pl1.term == lemma then
if plurals then
insert(data.inflections, {label = "usually " .. glossary_link("invariable"),
q = pl1.q, qq = pl1.qq, l = pl1.l, ll = pl1.ll, refs = pl1.refs
})
table.remove(plurals, 1)
else
insert(data.inflections, {label = glossary_link("invariable"),
q = pl1.q, qq = pl1.qq, l = pl1.l, ll = pl1.ll, refs = pl1.refs
})
end
inscat("indeclinable " .. plpos)
end
if plurals then
-- Check for gender-changing plurals.
for _, pl in ipairs(plurals) do
if pl.genders then
for _, g in ipairs(pl.genders) do
if type(g) ~= "table" then
g = {spec = g}
end
if g.spec == "m" and not saw_m or g.spec == "f" and not saw_f then
inscat(plpos .. " that change gender in the plural")
end
end
end
end
-- We already took care of invariable plurals above
insert_noun_inflection(plurals, "plural", "p", "noinv")
end
end
-- Gather masculines/feminines. For each one, generate the corresponding plural. `field` is the name of the field
-- containing the masculine or feminine forms (normally "m" or "f"); `inflect` is a function of one or two arguments
-- to generate the default masculine or feminine from the lemma (the arguments are the lemma and optionally a
-- "special" flag to indicate how to handle multiword lemmas, and the function is normally make_feminine or
-- make_masculine from ]); and `default_plurals` is a list into which the corresponding default
-- plurals of the gathered or generated masculine or feminine forms are stored.
local function handle_mf(field, inflect, default_plurals)
local mfs = m_headword_utilities.parse_term_list_with_modifiers {
paramname = field,
forms = args,
frob = function(term)
if term == "+" then
-- Generate default masculine/feminine.
term = inflect(lemma)
else
term = replace_hash_with_lemma(term, lemma)
end
local special = require(romut_module).get_special_indicator(term)
if special then
term = inflect(lemma, special)
end
return term
end
}
for _, mf in ipairs(mfs) do
local plobj = m_table.shallowCopy(mf)
plobj.term = com.make_plural(mf.term, field, special)
if plobj.term then
-- Add an accelerator for each masculine/feminine plural whose lemma is the corresponding singular, so that
-- the accelerated entry that is generated has a definition that looks like
-- # {{plural of|it|MFSING}}
plobj.accel = {form = "p", lemma = mf.term}
insert(default_plurals, plobj)
end
end
return mfs
end
local feminine_plurals = {}
local feminines = handle_mf("f", com.make_feminine, feminine_plurals)
local masculine_plurals = {}
local masculines = handle_mf("m", com.make_masculine, masculine_plurals)
local function handle_mf_plural(mfplfield, mfpls, gender, default_plurals, singulars)
if is_plurale_tantum then
return mfpls, true
end
local new_mfpls = {}
local saw_plus
local noinv
for i, mfpl in ipairs(mfpls) do
local accel
if #mfpls == #singulars then
-- If same number of overriding masculine/feminine plurals as singulars, assume each plural goes with
-- the corresponding singular and use each corresponding singular as the lemma in the accelerator. The
-- generated entry will have
-- # {{plural of|it|SINGULAR}}
-- as the definition.
accel = {form = "p", lemma = singulars.term}
else
accel = nil
end
if mfpl.term == "+" then
-- We should never see + twice. If we do, it will lead to problems since we overwrite the values of
-- default_plurals the first time around.
if saw_plus then
error(("Saw + twice when handling %s="):format(mfplfield))
end
saw_plus = true
if not default_plurals then
local defpl = com.make_plural(lemma, gender)
if not defpl then
error("Unable to generate default plural of '" .. lemma .. "'")
end
default_plurals = {{term = defpl}}
end
for _, defpl in ipairs(default_plurals) do
-- defpl is already a table and has an accel field
m_headword_utilities.combine_termobj_qualifiers_labels(defpl, mfpl)
insert(new_mfpls, defpl)
end
-- don't use "invariable" because the plural is not with respect to the lemma but with respect to the
-- masc/fem singular
noinv = true
if #singulars > 0 then
for _, mf in ipairs(singulars) do
-- mf is a table
local default_mfpl = com.make_plural(mf.term_for_further_inflection, gender, mfpl)
if default_mfpl then
-- don't use "invariable" because the plural is not with respect to the lemma but
-- with respect to the masc/fem singular
insert_infl(default_mfpl, accel, mf.qualifiers, "no inv")
end
end
else
end
elseif mfpl.term == "cap*" or mfpl.term == "cap*+" or mfpl.term:find("^%+") then
if mfpl.term:find("^%+") then
mfpl.term = require(romut_module).get_special_indicator(mfpl.term)
end
if singulars then
for _, mf in ipairs(singulars) do
local mfplobj = m_table.shallowCopy(mfpl)
mfplobj.term = com.make_plural(mf.term_for_further_inflection, gender, mfpl.term)
if mfplobj.term then
mfplobj.accel = accel
m_headword_utilities.combine_termobj_qualifiers_labels(mfplobj, mf)
insert(new_mfpls, mfplobj)
end
-- don't use "invariable" because the plural is not with respect to the lemma but with respect
-- to the masc/fem singular
noinv = true
-- FIXME: Should we throw an error if no plural could be generated?
end
else
-- FIXME: This clause didn't exist in the corresponding code in ]. Is it
-- correct?
mfpl.term = com.make_plural(lemma, gender, mfpl.term)
if mfpl.term then
insert(new_mfpls, mfpl)
end
end
else
mfpl.accel = accel
mfpl.term = replace_hash_with_lemma(mfpl.term, lemma)
insert(new_mfpls, mfpl)
-- don't use "invariable" if masc/fem singular present because the plural is not with respect to
-- the lemma but with respect to the masc/fem singular
noinv = noinv or #singulars > 0
end
end
return new_mfpls, noinv
end
local mpl_noinv, fpl_noinv
-- Not fpls because if the user didn't specify any explicit mpl= or fpl= but the lemma gender is mf or mfbysense
-- and has separate masculine and feminine plural forms (e.g. any term in -ista), we don't want to reprocess those
-- auto-generated forms.
if args.fpl then
-- Override any existing feminine plurals.
feminine_plurals, fpl_noinv = handle_mf_plural("fpl", fpls, "f", feminine_plurals, feminines)
else
feminine_plurals, fpl_noinv = fpls, false
end
if args.mpl then
-- Override any existing masculine plurals.
masculine_plurals, mpl_noinv = handle_mf_plural("mpl", mpls, "m", masculine_plurals, masculines)
else
masculine_plurals, mpl_noinv = mpls, false
end
local function parse_and_insert_noun_inflection(field, label, accel)
parse_and_insert_inflection(data, args, field, label, plpos, accel)
end
local function redundant_plural(pl)
for _, p in ipairs(plurals) do
if p.term_for_further_inflection == pl.term_for_further_inflection then
return true
end
end
return false
end
for _, mpl in ipairs(masculine_plurals) do
if redundant_plural(mpl) then
track("noun-redundant-mpl")
end
end
for _, fpl in ipairs(feminine_plurals) do
if redundant_plural(fpl) then
track("noun-redundant-fpl")
end
end
insert_noun_inflection(masculines, "masculine")
insert_noun_inflection(masculine_plurals, "masculine plural", nil, mpl_noinv)
insert_noun_inflection(feminines, "feminine", "f")
insert_noun_inflection(feminine_plurals, "feminine plural", nil, fpl_noinv)
parse_and_insert_noun_inflection("dem", "demonym")
parse_and_insert_noun_inflection("fdem", "female demonym")
insert_deriv_inflections(data, args, plpos)
-- Maybe add category 'Italian nouns with irregular gender' (or similar)
local irreg_gender_lemma = lemma:gsub(" .*", "") -- only look at first word
if (irreg_gender_lemma:find("o$") and (gender_for_default_plural == "f" or gender_for_default_plural == "mf"
or gender_for_default_plural == "mfbysense")) or
(irreg_gender_lemma:find("a$") and (gender_for_default_plural == "m" or gender_for_default_plural == "mf"
or gender_for_default_plural == "mfbysense")) then
inscat(plpos .. " with irregular gender")
end
end
local function get_noun_params(nountype)
local params = {
= {list = "g", disallow_holes = true, required = nountype ~= "proper", default = "?", type = "genders",
flatten = true},
= {list = "pl", disallow_holes = true},
= {list = "pl\1_g", allow_holes = true},
= list_param,
= list_param,
= list_param,
= list_param,
= list_param, --demonym(s)
= list_param, --female demonym(s)
}
insert_deriv_params(params)
return params
end
pos_functions = {
params = get_noun_params("base"),
func = function(args, data, is_suffix)
do_noun(args, data, "noun", is_suffix)
end,
}
pos_functions = {
params = get_noun_params("proper"),
func = function(args, data, is_suffix)
do_noun(args, data, "proper noun", is_suffix, "is proper noun")
end,
}
pos_functions = {
params = get_noun_params("base"),
func = function(args, data, is_suffix)
do_noun(args, data, "numeral")
data.pos_category = "numerals"
insert(data.categories, 1, langname .. " cardinal numbers")
end,
}
-----------------------------------------------------------------------------------------
-- Adjectives --
-----------------------------------------------------------------------------------------
local function do_adjective(args, data, pos, is_suffix, is_superlative)
local feminines = {}
local masculine_plurals = {}
local feminine_plurals = {}
if is_suffix then
pos = "suffix"
end
local plpos = m_en_utilities.pluralize(pos)
if not is_suffix then
data.pos_category = plpos
end
if args.sp then
local romut = require(romut_module)
if not romut.allowed_special_indicators then
local indicators = {}
for indic, _ in pairs(romut.allowed_special_indicators) do
insert(indicators, "'" .. indic .. "'")
end
table.sort(indicators)
error("Special inflection indicator beginning can only be " ..
mw.text.listToText(indicators) .. ": " .. args.sp)
end
end
local lemma = data.pagename
local function fetch_inflections(field)
local retval = m_headword_utilities.parse_term_list_with_modifiers {
paramname = field,
forms = args,
}
if not retval then
return {{term = "+"}}
end
return retval
end
local function insert_inflection(terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
check_missing = true,
lang = lang,
plpos = plpos,
}
end
if args.inv then
-- invariable adjective
insert(data.inflections, {label = glossary_link("invariable")})
insert(data.categories, langname .. " indeclinable " .. plpos)
end
if args.noforms then
-- ] and any others too complicated to describe in headword
insert(data.inflections, {label = "see below for inflection"})
end
if args.inv or args.apoc or args.noforms then
if args.sp or args.f or args.pl or args.mpl or args.fpl then
error("Can't specify inflections with an invariable or apocopated adjective or with noforms=")
end
elseif args.fonly then
-- feminine-only
if args.f then
error("Can't specify explicit feminines with feminine-only " .. pos)
end
if args.pl then
error("Can't specify explicit plurals with feminine-only " .. pos .. ", use fpl=")
end
if args.mpl then
error("Can't specify explicit masculine plurals with feminine-only " .. pos)
end
local argsfpl = fetch_inflections("fpl")
for _, fpl in ipairs(argsfpl) do
if fpl.term == "+" then
local defpl = com.make_plural(lemma, "f", args.sp)
if not defpl then
error("Unable to generate default plural of '" .. lemma .. "'")
end
fpl.term = defpl
else
fpl.term = replace_hash_with_lemma(fpl.term, lemma)
end
insert(feminine_plurals, fpl)
end
insert(data.inflections, {label = "feminine-only"})
insert_inflection(feminine_plurals, "feminine plural", "f|p")
else
-- Gather feminines.
for _, f in ipairs(fetch_inflections("f")) do
if f.term == "+" then
-- Generate default feminine.
f.term = com.make_feminine(lemma, args.sp)
else
f.term = replace_hash_with_lemma(f.term, lemma)
end
insert(feminines, f)
end
local fem_like_lemma = #feminines == 1 and feminines.term == lemma and
not m_headword_utilities.termobj_has_qualifiers_or_labels(feminines)
if fem_like_lemma then
insert(data.categories, langname .. " epicene " .. plpos)
end
local mpl_field = "mpl"
local fpl_field = "fpl"
if args.pl then
if args.mpl or args.fpl then
error("Can't specify both pl= and mpl=/fpl=")
end
mpl_field = "pl"
fpl_field = "pl"
end
local argsmpl = fetch_inflections(mpl_field)
local argsfpl = fetch_inflections(fpl_field)
for _, mpl in ipairs(argsmpl) do
if mpl.term == "+" then
-- Generate default masculine plural.
local defpl = com.make_plural(lemma, "m", args.sp)
if not defpl then
error("Unable to generate default plural of '" .. lemma .. "'")
end
mpl.term = defpl
else
mpl.term = replace_hash_with_lemma(mpl.term, lemma)
end
insert(masculine_plurals, mpl)
end
for _, fpl in ipairs(argsfpl) do
if fpl.term == "+" then
for _, f in ipairs(feminines) do
-- Generate default feminine plural; f is a table.
local fplobj = m_table.shallowCopy(fpl)
local defpl = com.make_plural(f.term, "f", args.sp)
if not defpl then
error("Unable to generate default plural of '" .. f.term .. "'")
end
fplobj.term = defpl
m_headword_utilities.combine_termobj_qualifiers_labels(fplobj, f)
insert(feminine_plurals, fplobj)
end
else
fpl.term = replace_hash_with_lemma(fpl.term, lemma)
insert(feminine_plurals, fpl)
end
end
local fem_pl_like_masc_pl = masculine_plurals and feminine_plurals and
m_table.deepEquals(masculine_plurals, feminine_plurals)
local masc_pl_like_lemma = #masculine_plurals == 1 and masculine_plurals.term == lemma and
not m_headword_utilities.termobj_has_qualifiers_or_labels(masculine_plurals)
if fem_like_lemma and fem_pl_like_masc_pl and masc_pl_like_lemma then
-- actually invariable
insert(data.inflections, {label = glossary_link("invariable")})
insert(data.categories, langname .. " indeclinable " .. plpos)
else
-- Make sure there are feminines given and not same as lemma.
if not fem_like_lemma then
insert_inflection(feminines, "feminine", "f|s")
elseif args.gneut then
data.genders = {"gneut"}
else
data.genders = {"mf"}
end
if fem_pl_like_masc_pl then
if args.gneut then
insert_inflection(masculine_plurals, "plural", "p")
else
-- This is how the Spanish module works.
-- insert_inflection(masculine_plurals, "masculine and feminine plural", "p")
insert_inflection(masculine_plurals, "plural", "p")
end
else
insert_inflection(masculine_plurals, "masculine plural", "m|p")
insert_inflection(feminine_plurals, "feminine plural", "f|p")
end
end
end
local function parse_and_insert_adj_inflection(field, label, accel, frob)
parse_and_insert_inflection(data, args, field, label, plpos, accel, frob)
end
parse_and_insert_adj_inflection("n", "neuter")
parse_and_insert_adj_inflection("comp", "comparative")
parse_and_insert_adj_inflection("sup", "superlative")
insert_deriv_inflections(data, args, plpos)
if args.irreg and is_superlative then
insert(data.categories, langname .. " irregular superlative " .. plpos)
end
end
local function get_adjective_params(adjtype)
local params = {
= boolean_param, --invariable
= boolean_param, --too complicated to list forms except in a table
= true, -- special indicator: "first", "first-last", etc.
= list_param, --feminine form(s)
= list_param, --plural override(s)
= list_param, --feminine plural override(s)
= list_param, --masculine plural override(s)
}
if adjtype == "base" or adjtype == "part" or adjtype == "det" then
params = list_param --comparative(s)
params = list_param --superlative(s)
params = boolean_param -- feminine only
end
if adjtype == "sup" then
params = boolean_param
end
insert_deriv_params(params)
return params
end
pos_functions = {
params = get_adjective_params("base"),
func = function(args, data, is_suffix)
do_adjective(args, data, "adjective", is_suffix)
end,
}
pos_functions = {
params = get_adjective_params("comp"),
func = function(args, data, is_suffix)
do_adjective(args, data, "adjective", is_suffix)
end,
}
pos_functions = {
params = get_adjective_params("sup"),
func = function(args, data, is_suffix)
do_adjective(args, data, "adjective", is_suffix, "is superlative")
end,
}
pos_functions = {
params = get_adjective_params("card"),
func = function(args, data, is_suffix)
do_adjective(args, data, "numeral", is_suffix)
insert(data.categories, 1, langname .. " cardinal numbers")
end,
}
pos_functions = {
params = get_adjective_params("part"),
func = function(args, data, is_suffix)
do_adjective(args, data, "participle", is_suffix)
data.pos_category = "past participles"
end,
}
pos_functions = {
params = get_adjective_params("part"),
func = function(args, data, is_suffix)
do_adjective(args, data, "participle", is_suffix)
data.pos_category = "present participles"
end,
}
pos_functions = {
params = get_adjective_params("det"),
func = function(args, data, is_suffix)
do_adjective(args, data, "determiner", is_suffix)
end,
}
pos_functions = {
params = get_adjective_params("det"),
func = function(args, data, is_suffix)
do_adjective(args, data, "article", is_suffix)
end,
}
pos_functions = {
params = get_adjective_params("pron"),
func = function(args, data, is_suffix)
do_adjective(args, data, "pronoun", is_suffix)
end,
}
pos_functions = {
params = {},
func = function(args, data)
data.pos_category = "numerals"
insert(data.categories, langname .. " cardinal numbers")
insert(data.categories, langname .. " indeclinable numerals")
insert(data.inflections, {label = glossary_link("invariable")})
end,
}
-----------------------------------------------------------------------------------------
-- Adverbs --
-----------------------------------------------------------------------------------------
local function do_adverb(args, data, pos, is_suffix)
if is_suffix then
pos = "suffix"
end
local plpos = m_en_utilities.pluralize(pos)
if not is_suffix then
data.pos_category = plpos
end
local function parse_and_insert_adv_inflection(field, label, accel, frob)
parse_and_insert_inflection(data, args, field, label, plpos, accel, frob)
end
parse_and_insert_adv_inflection("comp", "comparative")
parse_and_insert_adv_inflection("sup", "superlative")
end
local function get_adverb_params(advtype)
local params = {}
if advtype == "base" then
params = list_param --comparative(s)
params = list_param --superlative(s)
end
return params
end
pos_functions = {
params = get_adverb_params("base"),
func = function(args, data, is_suffix)
do_adverb(args, data, "adverb", is_suffix)
end,
}
pos_functions = {
params = get_adverb_params("comp"),
func = function(args, data, is_suffix)
do_adverb(args, data, "adverb", is_suffix)
end,
}
pos_functions = {
params = get_adverb_params("sup"),
func = function(args, data, is_suffix)
do_adverb(args, data, "adverb", is_suffix)
end,
}
-----------------------------------------------------------------------------------------
-- Verbs --
-----------------------------------------------------------------------------------------
pos_functions = {
params = {
= {},
= boolean_param,
= boolean_param,
},
func = function(args, data)
if args then
local preses, prets, parts
local def_forms
local alternant_multiword_spec = require(it_verb_module).do_generate_forms(args, "from headword", data.heads)
local function do_verb_form(slot, label, rowslot, rowlabel)
local forms = alternant_multiword_spec.forms
local retval
if alternant_multiword_spec.rowprops.all_defective then
if not alternant_multiword_spec.rowprops.defective then
-- No forms, but none expected; don't display anything
return
end
retval = {label = "no " .. rowlabel}
elseif not forms then
retval = {label = "no " .. label}
elseif alternant_multiword_spec.rowprops.all_unknown then
retval = {label = "unknown " .. rowlabel}
elseif forms.form == "?" then
retval = {label = "unknown " .. label}
else
-- Disable accelerators for now because we don't want the added accents going into the headwords.
-- FIXME: We now have support in ] to specify the target explicitly; we can use this
-- so we can add the accelerators back with a param to avoid the accents.
local accel_form = nil -- all_verb_slots
retval = {label = label, accel = accel_form and {form = accel_form} or nil}
local prev_footnotes = nil
-- If the footnotes for this form are the same as the footnotes for the preceding form or
-- contain the preceding footnotes, replace the footnotes that are the same with "ditto".
-- This avoids repetition on pages like ] where the form ''succedétti'' has a long
-- footnote which gets repeated in the traditional form ''succedètti'' (which also has the
-- footnote "").
for _, form in ipairs(forms) do
local quals, refs = require(inflection_utilities_module).
convert_footnotes_to_qualifiers_and_references(form.footnotes)
local quals_with_ditto = quals
if quals and prev_footnotes then
local quals_contains_previous = true
for _, qual in ipairs(prev_footnotes) do
if not m_table.contains(quals, qual) then
quals_contains_previous = false
break
end
end
if quals_contains_previous then
local inserted_ditto = false
quals_with_ditto = {}
for _, qual in ipairs(quals) do
if m_table.contains(prev_footnotes, qual) then
if not inserted_ditto then
insert(quals_with_ditto, "ditto")
inserted_ditto = true
end
else
insert(quals_with_ditto, qual)
end
end
end
end
prev_footnotes = quals
insert(retval, {term = form.form, q = quals_with_ditto, refs = refs})
end
end
insert(data.inflections, retval)
end
if alternant_multiword_spec.props.is_pronominal then
insert(data.inflections, {label = glossary_link("pronominal")})
end
if alternant_multiword_spec.props.impers then
insert(data.inflections, {label = glossary_link("impersonal")})
end
if alternant_multiword_spec.props.thirdonly then
insert(data.inflections, {label = "third-person only"})
end
local thirdonly = alternant_multiword_spec.props.impers or alternant_multiword_spec.props.thirdonly
local sing_label = thirdonly and "third-person singular" or "first-person singular"
for _, rowspec in ipairs {
{"pres", "present", true},
{"phis", "past historic", true},
{"pp", "past participle", true},
{"imperf", "imperfect"},
{"fut", "future"},
{"sub", "subjunctive"},
{"impsub", "imperfect subjunctive"},
} do
local rowslot, desc, always_show = unpack(rowspec)
local slot = rowslot .. (thirdonly and "3s" or "1s")
local must_show = alternant_multiword_spec.is_irreg
if always_show then
must_show = true
elseif rowslot == "imperf" and alternant_multiword_spec.props.has_explicit_stem_spec then
-- If there is an explicit stem spec, make sure it gets displayed; the imperfect is a good way of
-- showing this.
must_show = true
elseif not alternant_multiword_spec.forms then
-- If the principal part is unexpectedly missing, make sure we show this.
must_show = true
elseif alternant_multiword_spec.forms.form == "?" then
-- If the principal part is unknown, make sure we show this.
must_show = true
end
if must_show then
if rowslot == "pp" then
do_verb_form(rowslot, desc, rowslot, desc)
else
do_verb_form(slot, sing_label .. " " .. desc, rowslot, desc)
end
end
end
-- Also do the imperative, but not for third-only verbs, which are always missing the imperative.
if not thirdonly and (alternant_multiword_spec.is_irreg.imp2s
or not alternant_multiword_spec.forms.imp2s) then
do_verb_form("imp2s", "second-person singular imperative", "imp", "imperative")
end
-- If there is a past participle but no auxiliary (e.g. ]), explicitly add "no auxiliary". In
-- cases where there's no past participle and no auxiliary (e.g. ]), we don't do this as we
-- already get "no past participle" displayed. Don't display an auxiliary in any case if the lemma
-- consists entirely of reflexive verbs (for which the auxiliary is always ]).
if alternant_multiword_spec.props.is_non_reflexive and (
alternant_multiword_spec.forms.aux or alternant_multiword_spec.forms.pp
) then
do_verb_form("aux", "auxiliary", "aux", "auxiliary")
end
-- Add categories.
for _, cat in ipairs(alternant_multiword_spec.categories) do
insert(data.categories, cat)
end
-- If the user didn't explicitly specify head=, or specified exactly one head (not 2+) and we were able to
-- incorporate any links in that head into the 1= specification, use the infinitive generated by
-- ] it in place of the user-specified or auto-generated head so that we get accents marked
-- on the verb(s). Don't do this if the user gave multiple heads or gave a head with a multiword-linked
-- verbal expression such as '] ] ]'.
if #data.user_specified_heads == 0 or (
#data.user_specified_heads == 1 and alternant_multiword_spec.incorporated_headword_head_into_lemma
) then
data.heads = {}
for _, lemma_obj in ipairs(alternant_multiword_spec.forms.inf) do
local quals, refs = require(inflection_utilities_module).
convert_footnotes_to_qualifiers_and_references(lemma_obj.footnotes)
insert(data.heads, {term = lemma_obj.form, q = quals, refs = refs})
end
end
end
end
}
-----------------------------------------------------------------------------------------
-- Suffix forms --
-----------------------------------------------------------------------------------------
pos_functions = {
params = {
= {required = true, list = true, disallow_holes = true},
= {list = true, disallow_holes = true, type = "genders", flatten = true},
},
func = function(args, data, is_suffix)
validate_genders(args.g)
data.genders = args.g
local suffix_type = {}
for _, typ in ipairs(args) do
insert(suffix_type, typ .. "-forming suffix")
end
insert(data.inflections, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})})
end,
}
-----------------------------------------------------------------------------------------
-- Arbitrary parts of speech --
-----------------------------------------------------------------------------------------
pos_functions = {
params = {
= {required = true},
= {list = true, disallow_holes = true, type = "genders", flatten = true},
},
func = function(args, data, is_suffix)
if is_suffix then
error("Can't use ] with suffixes")
end
validate_genders(args.g)
data.genders = args.g
local plpos = m_en_utilities.pluralize(args)
data.pos_category = plpos
end,
}
return export