This module implements headword templates for all Lechitic languages: Polish, Sorbian, Kashubian, Silesian, Masurian, Old Polish, Polabian and Slovincian. The parts of speech supported are nouns, proper nouns, verbs, adjectives, adverbs and participles.
The Wikicode of a template invoking this module should look similarly to the following (shown is the code for Silesian nouns, i.e. {{szl-noun}}
{{wen-noun}}
):
{{#invoke:zlw-lch-headword|show|nouns|lang=szl|lang=wen}}<!-- --><noinclude>{{documentation}}</noinclude>
local export = {}
local pos_functions = {}
local parse_utilities_module = "Module:parse utilities"
local rfind = mw.ustring.find
local rsplit = mw.text.split
local langs_supported = {
= {
peri_comp = "bardziej",
sup = "naj",
-- participle endings
act = {"ąc$"}, -- biegnący
pass = {"$"}, -- otwarty, uwielbiany, legły
cont_adv = {"ąc$"},
ant_adv = {"szy$"},
},
= {
peri_comp = "barżi",
sup = "nô",
-- participle endings
act = {"ący$"},
pass = {"ny$", "ty$", "łi$"},
cont_adv = {"ōnc$"},
ant_adv = {"szë$"},
},
= {
peri_comp = "bardzij",
sup = "noj",
-- participle endings
act = {"ōncy$"},
pass = {"ny$", "y$"},
cont_adv = {"ąc$"},
ant_adv = false,
},
= {
peri_comp = "barziej",
sup = false,
-- participle endings
act = {"ąc$"}, -- biegnący
pass = {"$"}, -- otwarty, uwielbiany, legły
cont_adv = {"ąc$"},
ant_adv = {"szy$"},
},
= {
peri_comp = false,
sup = false,
-- participle endings
act = false,
pass = false,
cont_adv = false,
ant_adv = false,
has_dual = true,
},
= {
peri_comp = "barżé",
sup = "no",
-- participle endings
act = false,
pass = false,
cont_adv = false,
ant_adv = false,
has_dual = true,
},
}
----------------------------------------------- Utilities --------------------------------------------
local function track(page)
require("Module:debug").track("zlw-lch-headword/" .. page)
return true
end
local function glossary_link(entry, text)
text = text or entry
return "]"
end
local param_mods = {
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",
convert = function(arg, parse_err)
return rsplit(arg, ",")
end,
},
id = {},
q = {store = "insert"},
qq = {store = "insert"},
}
-- Parse the inflections specified by the raw arguments in `infls`. `pagename` is the pagename, used to substitute
-- # in arguments. Parse inline modifiers attached to the raw arguments. Return `infls` if there are any inflections,
-- otherwise nil. WARNING: Destructively modifies `infls`.
local function parse_inflection(infls, pagename)
local function generate_obj(term, parse_err)
return {term = term:gsub("#", pagename)}
end
for i, infl in ipairs(infls) do
-- Check for inline modifier, e.g. acetylenowo<q:rare>.
if infl:find("<") then
infl = require(parse_utilities_module).parse_inline_modifiers(infl, {
param_mods = param_mods,
generate_obj = generate_obj,
})
else
infl = generate_obj(infl)
end
infls = infl
end
if #infls > 0 then
return infls
else
return nil
end
end
-- Insert the parsed inflections in `infls` (as parsed by `parse_inflection`) into `data.inflections`, with label
-- `label` and optional accelerator spec `accel`.
local function insert_inflection(data, infls, label, accel)
if infls and #infls > 0 then
if #infls == 1 and (infls == "-" or infls.term == "-") then
if infls.q then
error(("Can't specify qualifiers with the value '-' for %s"):format(label))
end
table.insert(data.inflections, {label = "no " .. label})
else
infls.label = label
infls.accel = accel
table.insert(data.inflections, infls)
end
end
end
----------------------------------------------- Main entry point --------------------------------------------
function export.show(frame)
local iparams = {
= {required = true},
= {required = true},
}
local iargs = require("Module:parameters").process(frame.args, iparams)
local poscat = iargs
langcode = iargs.lang
if not langs_supported then
local langcodes_supported = {}
for lang, _ in pairs(langs_supported) do
table.insert(langcodes_supported, lang)
end
error("This module currently only works for lang=" .. table.concat(langcodes_supported, "/"))
end
local lang = require("Module:languages").getByCode(langcode)
local langname = lang:getCanonicalName()
local params = {
= {list = true},
= {type = "boolean"},
= {type = "boolean", alias_of = "nolink"},
= {type = "boolean"},
= {type = "boolean"},
= {type = "boolean"},
= {list = true},
= {}, -- for testing
}
if pos_functions then
local posparams = pos_functions.params
if type(posparams) == "function" then
posparams = posparams(langcode)
end
for key, val in pairs(posparams) do
params = val
end
end
local parargs = frame:getParent().args
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.nolink then
if #heads == 0 then
heads = {pagename}
end
end
local data = {
lang = lang,
langcode = langcode,
langname = langname,
pos_category = poscat,
categories = {},
heads = heads,
user_specified_heads = user_specified_heads,
no_redundant_head_cat = #user_specified_heads == 0,
genders = {},
inflections = {},
categories = {},
pagename = pagename,
id = args.id,
force_cat_output = force_cat,
}
data.is_suffix = false
if args.suffix or (
not args.nosuffix and pagename:find("^%-") and poscat ~= "suffixes" and poscat ~= "suffix forms"
) then
data.is_suffix = true
data.pos_category = "suffixes"
local singular_poscat = require("Module:string utilities").singularize(poscat)
table.insert(data.categories, langname .. " " .. singular_poscat .. "-forming suffixes")
table.insert(data.inflections, {label = singular_poscat .. "-forming suffix"})
end
if pos_functions then
pos_functions.func(args, data)
end
local abbrs = parse_inflection(args.abbr, pagename)
insert_inflection(data, abbrs, "abbreviation")
if args.json then
return require("Module:JSON").toJSON(data)
end
return require("Module:headword").full_headword(data)
end
----------------------------------------------- Nouns --------------------------------------------
local function get_noun_inflection_specs(langcode)
local noun_inflection_specs = {
{"gen", "genitive singular"},
}
if langs_supported.has_dual then
table.insert(noun_inflection_specs, {"du", "nominative dual"})
end
for _, spec in ipairs {
{"pl", "nominative plural"},
{"genpl", "genitive plural"},
{"f", "female equivalent"},
{"m", "male equivalent"},
{"n", "neuter equivalent"},
{"marr", "traditional married form"},
{"unmarr", "traditional unmarried form"},
{"dim", "diminutive"},
{"pej", "pejorative"},
{"aug", "augmentative"},
{"adj", "related adjective"},
{"poss", "possessive adjective"},
{"dem", "demonym"},
{"fdem", "female demonym"},
} do
table.insert(noun_inflection_specs, spec)
end
return noun_inflection_specs
end
local function get_noun_pos(is_proper)
return {
params = function(langcode)
local params = {
= {type = "boolean"},
= {list = "g"},
}
for _, spec in ipairs(get_noun_inflection_specs(langcode)) do
local param, desc = unpack(spec)
params = {list = true, disallow_holes = true}
end
params = {list = true, disallow_holes = true, alias_of = "adj"}
return params
end,
func = function(args, data)
-- Compute allowed genders, and map incomplete genders to specs with a "?" in them.
local genders = {false, "m", "mf", "mfbysense", "f", "n", "g!"}
local animacies = {false, "in", "anml", "pr", "an!"}
local numbers = {false, "p", "num!"}
local allowed_genders = {}
for _, g in ipairs(genders) do
for _, an in ipairs(animacies) do
for _, num in ipairs(numbers) do
local source_gender_parts = {}
local dest_gender_parts = {}
local function ins_part(part, partname)
if part then
table.insert(source_gender_parts, part)
table.insert(dest_gender_parts, part)
elseif partname == "g" and num == false or
partname == "an" and g ~= "f" and g ~= "n" then
-- allow incomplete gender plurale tantum nouns; also allow incomplete
-- animacy for fem/neut, where it makes no difference for agreement
-- purposes; otherwise insert a ? to indicate incomplete gender spec
table.insert(dest_gender_parts, "?")
end
end
ins_part(g, "g")
ins_part(an, "an")
ins_part(num, "num")
if #source_gender_parts == 0 then
allowed_genders = "?"
else
allowed_genders =
table.concat(dest_gender_parts, "-")
end
-- "Virile" = masculine personal, allow in the plural and convert appropriately;
-- "Nonvirile" = anything but masculine personal, allow in the plural;
-- "Nonpersonal" = anything but personal, i.e. animal or inanimate; allow in the plural.
allowed_genders = "m-pr-p"
allowed_genders = "nv-p"
allowed_genders = "np-p"
end
end
end
-- Gather, validate and canonicalize genders
for _, gspec in ipairs(args) do
for _, g in ipairs(rsplit(gspec, ",")) do
if not allowed_genders then
error("Unrecognized " .. data.langname .. " gender: " .. g)
else
table.insert(data.genders, allowed_genders)
end
end
end
if args.indecl then
table.insert(data.inflections, {label = glossary_link("indeclinable")})
table.insert(data.categories, data.langname .. " indeclinable nouns")
end
-- Process all inflections.
for _, spec in ipairs(get_noun_inflection_specs(data.langcode)) do
local param, desc = unpack(spec)
local infls = parse_inflection(args, data.pagename)
insert_inflection(data, infls, desc)
end
end
}
end
pos_functions = get_noun_pos(false)
pos_functions = get_noun_pos(true)
----------------------------------------------- Verbs --------------------------------------------
local function get_verb_pos()
local verb_inflection_specs = {
-- order per old ]
{"det", "imperfective determinate"},
{"pf", "perfective"},
{"impf", "imperfective"},
{"indet", "indeterminate"},
{"freq", "frequentative"},
}
local params = {
= {default = "?"},
= {type = "boolean"},
}
for _, spec in ipairs(verb_inflection_specs) do
local param, desc = unpack(spec)
params = {list = true, disallow_holes = true}
end
return {
params = params,
func = function(args, data)
local allowed_aspects = require("Module:table").listToSet {
"pf", "impf", "biasp", "both", "impf-det", "impf-indet", "impf-freq", "?"
}
-- Gather aspects
for _, a in ipairs(rsplit(args, ",")) do
table.insert(data.genders, a)
end
local impf_allowed = true
local pf_allowed = true
local indet_allowed = true
local det_allowed = true
local freq_allowed = true
local function insert_label_and_cat(typ)
table.insert(data.inflections, {label = glossary_link(typ)})
table.insert(data.categories, data.langname .. " " .. typ .. " verbs")
end
-- Validate and canonicalize aspects.
for i, a in ipairs(data.genders) do
if not allowed_aspects then
error("Unrecognized " .. data.langname .. " aspect: " .. a)
elseif a == "both" then
a = "biasp"
elseif a == "impf-det" then
a = "impf"
insert_label_and_cat("determinate")
det_allowed = false
elseif a == "impf-indet" then
a = "impf"
insert_label_and_cat("indeterminate")
indet_allowed = false
elseif a == "impf-freq" then
a = "impf"
insert_label_and_cat("indeterminate")
insert_label_and_cat("frequentative")
indet_allowed = false
freq_allowed = false
elseif a == "pf" then
pf_allowed = false
elseif a == "impf" then
impf_allowed = false
end
data.genders = a
end
if args.def then
insert_label_and_cat("defective")
end
-- Process all inflections.
for _, spec in ipairs(verb_inflection_specs) do
local param, desc = unpack(spec)
local infls = parse_inflection(args, data.pagename)
if infls then
if param == "pf" and not pf_allowed then
error("Aspectual-pair perfectives not allowed with perfective-only verb")
end
if param == "impf" and not impf_allowed then
error("Aspectual-pair imperfectives not allowed with imperfective-only verb")
end
if param == "det" and not det_allowed then
error("Aspectual-pair determinates not allowed with imperfective-only determinate verb")
end
if param == "indet" and not indet_allowed then
error("Aspectual-pair indeterminates not allowed with imperfective-only indeterminate or frequentative verb")
end
if param == "freq" and not freq_allowed then
error("Aspectual-pair frequentatives not allowed with imperfective-only frequentative verb")
end
insert_inflection(data, infls, desc)
end
end
end
}
end
pos_functions = get_verb_pos()
----------------------------------------------- Adjectives, Adverbs --------------------------------------------
local function get_adj_adv_pos(pos)
return {
params = function(langcode)
local params = {
= {list = true, disallow_holes = true},
= {list = true, disallow_holes = true},
= {list = true, disallow_holes = true},
= {type = "boolean"},
}
if pos == "adjective" then
params = {list = true, disallow_holes = true}
params = {type = "boolean"}
end
if langcode == "pl" then
params = {list = true, disallow_holes = true}
params = {list = true, disallow_holes = true}
end
return params
end,
func = function(args, data)
local default_sups = {}
local comps = parse_inflection(args, data.pagename)
if comps then
lang_data = langs_supported
if comps.term == "-" then
if comps.q then
error("Can't specify qualifiers with 1=-")
end
if #comps == 1 then
table.insert(data.inflections, {label = "not " .. glossary_link("comparable")})
table.insert(data.categories, data.langname .. " uncomparable " .. data.pos_category)
else
table.insert(data.inflections, {label = "not generally " .. glossary_link("comparable")})
end
table.remove(comps, 1)
end
for i, comp in ipairs(comps) do
if comp.term == "peri" then
if not lang_data.peri_comp then
error("Don't know how to form periphrastic comparatives for " .. data.langname)
end
comp.term = ("] ]"):format(lang_data.peri_comp, data.pagename)
if lang_data.sup then
table.insert(default_sups, {term = ("] ]"):format(
lang_data.sup, lang_data.peri_comp, data.pagename), q = comp.q, qq = comp.qq, id = comp.id})
end
elseif lang_data.sup then
table.insert(default_sups, {term = ("%s%s"):format(lang_data.sup, comp.term), q = comp.q, qq = comp.qq,
id = comp.id})
end
end
end
insert_inflection(data, comps, "comparative", {form = "comparative"})
local sups = parse_inflection(args.sup, data.pagename)
if not sups then
sups = args.nodefsup and {} or {{term = "+"}}
end
local combined_sups = {}
local function combine_qualifiers(q1, q2)
if not q1 then
return q2
end
if not q2 then
return q1
end
local combined = {}
for _, q in ipairs(q1) do
table.insert(combined, q)
end
for _, q in ipairs(q2) do
table.insert(combined, q)
end
return combined
end
for _, sup in ipairs(sups) do
if sup.term == "+" then
for _, def_sup in ipairs(default_sups) do
def_sup.q = combine_qualifiers(def_sup.q, sup.q)
def_sup.qq = combine_qualifiers(def_sup.qq, sup.qq)
def_sup.id = def_sup.id or sup.id
table.insert(combined_sups, def_sup)
end
else
table.insert(combined_sups, sup)
end
end
insert_inflection(data, combined_sups, "superlative", {form = "superlative"})
if data.langcode == "pl" then
local mpcomp = parse_inflection(args.mpcomp, data.pagename)
insert_inflection(data, mpcomp, "Middle Polish comparative")
local mpsup = parse_inflection(args.mpsup, data.pagename)
insert_inflection(data, mpsup, "Middle Polish superlative")
end
if pos == "adjective" then
if args.indecl then
table.insert(data.inflections, {label = glossary_link("indeclinable")})
table.insert(data.categories, data.langname .. " indeclinable adjectives")
end
local infls = parse_inflection(args.adv, data.pagename)
insert_inflection(data, infls, "derived adverb")
end
local infls = parse_inflection(args.dim, data.pagename)
insert_inflection(data, infls, "diminutive")
end,
}
end
pos_functions = get_adj_adv_pos("adjective")
pos_functions = get_adj_adv_pos("adverb")
----------------------------------------------- Participles --------------------------------------------
local function get_part_pos()
local params = {
= {},
= {list = true, disallow_holes = true},
}
return {
params = params,
func = function(args, data)
if data.langcode ~= "pl" then
error("Internal error: Unable to handle languages other than Polish for participles: " .. data.langname)
end
-- Compute allowed aspects, and map incomplete aspects to specs with a "?" in them.
local allowed_aspects = require("Module:table").listToSet {
"pf", "impf", "biasp", "both", "pf-it", "pf-sem", "impf-it", "impf-dur", "?"
}
local allowed_types = require("Module:table").listToSet {
"pass", "act", "ant-adv", "cont-adv", "?"
}
-- Gather aspects
data.genders = args.a
local function insert_label_and_cat(label, nolink)
if not nolink then
label = glossary_link(label)
end
table.insert(data.inflections, {label = label})
table.insert(data.categories, data.langname .. " " .. label .. " participles")
end
-- Validate and canonicalize aspects
for i, g in ipairs(data.genders) do
if not allowed_aspects then
error("Unrecognized " .. data.langname .. " participle aspect: " .. g)
elseif g == "both" then
g = "biasp"
elseif g == "impf-it" then
g = "impf"
insert_label_and_cat("iterative")
elseif g == "impf-dur" then
g = "impf"
insert_label_and_cat("durative")
elseif g == "pf-it" then
g = "pf"
insert_label_and_cat("iterative")
elseif g == "pf-sem" then
g = "pf"
insert_label_and_cat("semelfactive")
end
data.genders = g
end
-- Validate or autodetect participle type.
local function matches_parttype(typ)
local endings = langs_supported
if not endings then
return false
end
for _, ending in ipairs(endings) do
if rfind(data.pagename, ending) then
return true
end
end
return false
end
local ptype = args
if ptype then
if not allowed_types then
error("Unrecognized " .. data.langname .. " participle type: " .. ptype)
end
elseif matches_parttype("act") then -- biegnący
ptype = "act"
elseif matches_parttype("pass") then -- otwarty, uwielbiany, legły
ptype = "pass"
elseif matches_parttype("cont_adv") then
ptype = "cont-adv"
elseif matches_parttype("ant_adv") then
ptype = "ant-adv"
elseif (data.pagename:find("%-participle$") or data.pagename:find("%-part$")) and
mw.title.getCurrentTitle().nsText == "Template" then
ptype = "pass"
else
error(("Missing %s participle type and can't infer from pagename '%s'"):format(data.langname,
data.pagename))
end
if ptype == "act" then
insert_label_and_cat("active adjectival", true)
elseif ptype == "pass" then
insert_label_and_cat("passive adjectival", true)
elseif ptype == "cont-adv" then
insert_label_and_cat("contemporary adverbial", true)
elseif ptype == "ant-adv" then
insert_label_and_cat("anterior adverbial", true)
end
end
}
end
pos_functions = get_part_pos()
return export