Module:Es-headword

local export = {} local pos_functions = {}

local lang = require("Module:languages").getByCode("es")

local PAGENAME = mw.title.getCurrentTitle.text

local suffix_categories = { ["adjectives"] = true, ["adverbs"] = true, ["nouns"] = true, ["verbs"] = true, }

local remove_stress = { ["á"] = "a", ["é"] = "e", ["í"] = "i", ["ó"] = "o", ["ú"] = "u" } local add_stress = { ["a"] = "á", ["e"] = "é", ["i"] = "í", ["o"] = "ó", ["u"] = "ú" }

local function track(page) require("Module:debug").track("es-headword/" .. page) return true end

local function glossary_link(entry, text) text = text or entry return "" .. text .. "" end

-- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local tracking_categories = {} local poscat = frame.args[1] or error("Part of speech has not been specified. Please pass parameter 1 to the module invocation.") local params = { ["head"] = {list = true}, ["suff"] = {type = "boolean"}, ["json"] = {type = "boolean"}, }

local parargs = frame:getParent.args if poscat == "nouns" and (not parargs[2] or parargs[2] == "") and parargs.pl2 then track("noun-pl2-without-pl") end

if pos_functions[poscat] then for key, val in pairs(pos_functions[poscat].params) do			params[key] = val end end local args = require("Module:parameters").process(parargs, params) local data = { lang = lang, pos_category = poscat, categories = {}, heads = args["head"], genders = {}, inflections = {}, categories = {} }	if args["suff"] then data.pos_category = "suffixes" if suffix_categories[poscat] then local singular_poscat = poscat:gsub("s$", "") table.insert(data.categories, "Spanish " .. singular_poscat .. "-forming suffixes") else error("No category exists for suffixes forming " .. poscat .. ".") end end if pos_functions[poscat] then pos_functions[poscat].func(args, data, tracking_categories) end if args["json"] then return require("Module:JSON").toJSON(data) end

return require("Module:headword").full_headword(data) .. require("Module:utilities").format_categories(tracking_categories, lang) end

local function add_ending_to_plurals(plurals, ending) local retval = {} for _, pl in ipairs(plurals) do table.insert(retval, pl .. ending) end return retval end

function export.make_plural_noun(singular, gender) -- noun + adjective if singular:find(" ") then if singular:find(" del? .+$") then local preceding, prep_phrase = singular:match("^(.+)( del? .+)$") local preceding_pl = export.make_plural_noun(preceding, gender) if preceding_pl then return add_ending_to_plurals(preceding_pl, prep_phrase) else return nil end elseif singular:find(" al? .+$") then local preceding, prep_phrase = singular:match("^(.+)( al? .+)$")

local preceding_pl = export.make_plural_noun(preceding, gender) if preceding_pl then return add_ending_to_plurals(preceding_pl, prep_phrase) else return nil end else local words = mw.text.split(singular, " ") if #words == 2 then local noun_p = export.make_plural_noun(words[1], gender) local adj_p = export.adjective_forms(words[2], gender)

if noun_p then if gender == "m" and adj_p then return add_ending_to_plurals(noun_p, " " .. adj_p.mp) elseif gender == "f" and adj_p then return add_ending_to_plurals(noun_p, " " .. adj_p.fp) end end end end end -- ends in unstressed vowel or á, é, ó if mw.ustring.match(singular, "[aeiouáéó]$") then return {singular .. "s"} end -- ends in í or ú if mw.ustring.match(singular, "[íú]$") then return {singular .. "s", singular .. "es"} end -- ends in a vowel + z	if mw.ustring.match(singular, "[aeiouáéíóú]z$") then -- discard all but first return value local retval = mw.ustring.gsub(singular, "z$", "ces") return {retval} end -- ends in tz	if mw.ustring.match(singular, "tz$") then return {singular} end

local vowels = {} -- Replace qu before e or i with k so that the u isn't counted as a vowel. local modified_singular = mw.ustring.gsub(singular, "qu([ie])", "k%1") for i in mw.ustring.gmatch(modified_singular, "[aeiouáéíóú]") do vowels[#vowels + 1] = i end -- ends in s or x with more than 1 syllable, last syllable unstressed if vowels[2] and mw.ustring.match(singular, "[sx]$") and mw.ustring.match(vowels[#vowels], "[aeiou]") then return {singular} end -- ends in l, r, n, d, z, or j with 3 or more syllables, accented on third to last syllable if vowels[3] and mw.ustring.match(singular, "[lrndzj]$") and mw.ustring.match(vowels[#vowels-2], "[áéíóú]") then return {singular} end -- ends in a in a stressed vowel + consonant if mw.ustring.match(singular, "[áéíóú][^aeiouáéíóú]$") then -- discard all but first return value local retval = mw.ustring.gsub(			singular,			"(.)(.)$",			function (vowel, consonant)				return remove_stress[vowel] .. consonant .. "es"			end) return {retval} end -- ends in a vowel + y, l, r, n, d, j, s, x	if mw.ustring.match(singular, "[aeiou][ylrndjsx]$") then -- two or more vowels: add stress mark to plural if vowels[2] and mw.ustring.match(singular, "n$") then local before_stress, after_stress = mw.ustring.match(				modified_singular,				"^(.*)[aeiou]([^aeiou]*[aeiou][nl])$") local stress = add_stress[vowels[#vowels - 1]] if before_stress and stress then -- discard all but first return value local retval = (before_stress .. stress .. after_stress .. "es"):gsub("k", "qu") return {retval} end end return {singular .. "es"} end -- ends in a vowel + ch if mw.ustring.match(singular, "[aeiou]ch$") then return {singular .. "es"} end -- ends in two consonants if mw.ustring.match(singular, "[^aeiouáéíóú][^aeiouáéíóú]$") then return {singular .. "s"} end -- ends in a vowel + consonant other than l, r, n, d, z, j, s, or x	if mw.ustring.match(singular, "[aeiou][^aeioulrndzjsx]$") then return {singular .. "s"} end

return nil end

function export.adjective_forms(singular, gender) local last_two, last = mw.ustring.match(singular, "(.(.))$") if mw.ustring.match(singular, "dor$") and gender == "m" then return { ['ms'] = singular, ['mp'] = singular .. 'es', ['fs'] = singular .. 'a', ['fp'] = singular .. 'as' }	end if mw.ustring.match(singular, "dora$") and gender == "f" then local stem = mw.ustring.sub(singular, 1, #singular-1) return { ['ms'] = stem, ['mp'] = stem .. 'es', ['fs'] = stem .. 'a', ['fp'] = stem .. 'as' }	end if last == "o" or last == "a" and gender == "f" then local stem = mw.ustring.match(singular, "^(.+)[ao]$") return { ['ms'] = stem .. "o", ['mp'] = stem .. "os", ['fs'] = stem .. "a", ['fp'] = stem .. "as" }	end if last == "e" or mw.ustring.match(singular, "ista$") then local plural = singular..'s'		return { ['ms'] = singular, ['mp'] = plural, ['fs'] = singular, ['fp'] = plural }	end if last == "z" then local plural = mw.ustring.gsub(singular, "z$", "ces") return { ['ms'] = singular, ['mp'] = plural, ['fs'] = singular, ['fp'] = plural }	end local function make_stem(singular) return mw.ustring.gsub(			singular,			"^(.+)(.)(.)$",			function (before_stress, stressed_vowel, after_stress)				return before_stress .. (remove_stress[stressed_vowel] or stressed_vowel) .. after_stress			end) end if last_two == "ar" or last_two == "ón" or last_two == "ún" or last == "l" then local plural = make_stem(singular).."es" return { ['ms'] = singular, ['mp'] = plural, ['fs'] = singular, ['fp'] = plural }	end if last_two == "or" then return { ['ms'] = singular, ['mp'] = singular.."es", ['fs'] = singular, ['fp'] = singular.."es" }	end

if last_two == "án" or last_two == "és" or last_two == "ín" then local stem = make_stem(singular) return { ['ms'] = singular, ['mp'] = stem.."es", ['fs'] = stem.."a", ['fp'] = stem.."as" }	end return nil end

-- Display information for a noun's gender -- This is separate so that it can also be used for proper nouns function noun_gender(args, data) local gender = args[1] table.insert(data.genders, gender) if #data.genders == 0 then table.insert(data.genders, "?") end end

pos_functions["proper nouns"] = { params = { [1] = {},		},	func = function(args, data) noun_gender(args, data) end }

-- Display additional inflection information for a noun pos_functions["nouns"] = { params = { [1] = {required = true, default = "m"}, --gender ["g2"] = {}, --second gender ["e"] = {type = "boolean"}, --epicene [2] = {list = "pl"}, --plural override(s) ["f"] = {list = true}, --feminine form(s) ["m"] = {list = true}, --masculine form(s) ["fpl"] = {list = true}, --feminine plural override(s) ["mpl"] = {list = true}, --masculine plural override(s) ["title"] = {}, },	func = function(args, data, tracking_categories) local langname = lang:getCanonicalName

local allowed_genders = { ["m"] = true, ["f"] = true, ["m-p"] = true, ["f-p"] = true, ["mf"] = true, ["mf-p"] = true, ["mfbysense"] = true, ["mfbysense-p"] = true, }

if args["title"] then track("noun-title") end

local title = require("Module:links").remove_links(			args["title"] or (#data.heads > 0 and data.heads[1]) or mw.title.getCurrentTitle.text)

if args[1] == "m-f" then args[1] = "mf" elseif args[1] == "mfp" or args[1] == "m-f-p" then args[1] = "mf-p" end if not allowed_genders[args[1]] then error("Unrecognized gender: " .. args[1]) end

table.insert(data.genders, args[1])

local function check_missing(form) if type(form) == "table" then form = form.term end if form and not mw.title.new(form).exists then table.insert(tracking_categories, "Spanish nouns with red links in their headword lines") end end local function check_all_missing(forms) for _, form in ipairs(forms) do				check_missing(form) end end if args.g2 then table.insert(data.genders, args.g2) end if args["e"] then table.insert(data.categories, langname .. " epicene nouns") table.insert(data.inflections, {label = glossary_link("epicene")}) end

local plurals = {}

if args[1]:find("%-p$") then table.insert(data.inflections, {label = glossary_link("plural only")}) if #args[2] > 0 then error("Can't specify plurals of a plurale tantum noun") end else -- Gather plurals, handling requests for default plurals for _, pl in ipairs(args[2]) do				if pl == "1" then track("noun-pl-1") end if pl == "1" or pl == "+" then local default_pls = export.make_plural_noun(title, args[1]) for _, defp in ipairs(default_pls) do						table.insert(plurals, defp) end else table.insert(plurals, pl) end end

-- Check for special plural signals local mode = nil if plurals[1] == "?" or plurals[1] == "!" or plurals[1] == "-" or plurals[1] == "~" then mode = plurals[1] table.remove(plurals, 1) -- Remove the mode parameter end if mode == "?" then -- Plural is unknown table.insert(data.categories, langname .. " nouns with unknown or uncertain plurals") elseif mode == "!" then -- Plural is not attested table.insert(data.inflections, {label = "plural not attested"}) table.insert(data.categories, langname .. " nouns with unattested plurals") return elseif mode == "-" then -- Uncountable noun; may occasionally have a plural table.insert(data.categories, langname .. " uncountable nouns") -- If plural forms were given explicitly, then show "usually" if #plurals > 0 then table.insert(data.inflections, {label = "usually " .. glossary_link("uncountable")}) table.insert(data.categories, langname .. " countable nouns") else table.insert(data.inflections, {label = glossary_link("uncountable")}) end else -- Countable or mixed countable/uncountable if #plurals == 0 then local pls = export.make_plural_noun(title, args[1]) if pls then for _, pl in ipairs(pls) do							table.insert(plurals, pl) end end end if mode == "~" then -- Mixed countable/uncountable noun, always has a plural table.insert(data.inflections, {label = glossary_link("countable") .. " and " .. glossary_link("uncountable")}) table.insert(data.categories, langname .. " uncountable nouns") table.insert(data.categories, langname .. " countable nouns") else -- Countable nouns table.insert(data.categories, langname .. " countable nouns") end end end

local masculines = {} local feminines = {} local masculine_plurals = {} local feminine_plurals = {} -- Gather feminines. For each feminine, generate the corresponding plural(s). for _, f in ipairs(args.f) do			if f == "1" then track("noun-f-1") end if f == "1" or f == "+" then -- Generate default feminine. local noun_forms = export.adjective_forms(title, "m") if not noun_forms then error("Unable to generate default feminine of '" .. title .. "'") end f = noun_forms.fs			end table.insert(feminines, f)			local fpls = export.make_plural_noun(f, "f") if fpls then for _, pl in ipairs(fpls) do					-- Add an accelerator for each feminine plural whose lemma -- is the feminine singular, so that the accelerated entry -- that is generated has a definition that looks like -- # 					table.insert(feminine_plurals, {term = pl, accel = {form = "p", lemma = f}}) end end end -- Gather feminines. For each masculine, generate the corresponding plural(s). for _, m in ipairs(args.m) do			if m == "1" then track("noun-m-1") end if m == "1" or m == "+" then -- Generate default masculine. local noun_forms = export.adjective_forms(title, "f") if not noun_forms then error("Unable to generate default masculine of '" .. title .. "'") end m = noun_forms.ms			end table.insert(masculines, m)			local mpls = export.make_plural_noun(m, "m") if mpls then for _, pl in ipairs(mpls) do					table.insert(masculine_plurals, pl) end end end if #args.fpl > 0 then -- Override any existing feminine plurals. if #args.fpl == #feminines then -- If same number of overriding feminine plurals as feminines, -- assume each feminine plural goes with the corresponding feminine -- and use each corresponding feminine as the lemma in the accelerator. -- The generated entry will have # as the -- definition. feminine_plurals = {} for i, fpl in ipairs(args.fpl) do					table.insert(feminine_plurals, {term = fpl, accel = {form = "p", lemma = feminines[i]}}) end else -- Otherwise, don't add any accelerators. feminine_plurals = args.fpl end end if #args.mpl > 0 then -- Override any existing masculine plurals. masculine_plurals = args.mpl end check_all_missing(plurals) check_all_missing(feminines) check_all_missing(feminine_plurals) check_all_missing(masculines) check_all_missing(masculine_plurals)

local function redundant_plural(pl) for _, p in ipairs(plurals) do				if p == pl 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

if #plurals > 0 then plurals.label = "plural" plurals.accel = {form = "p"} table.insert(data.inflections, plurals) end if #feminines > 0 then feminines.label = "feminine" feminines.accel = {form = "f"} table.insert(data.inflections, feminines) end if #feminine_plurals > 0 then feminine_plurals.label = "feminine plural" table.insert(data.inflections, feminine_plurals) end if #masculines > 0 then masculines.label = "masculine" table.insert(data.inflections, masculines) end if #masculine_plurals > 0 then masculine_plurals.label = "masculine plural" masculine_plurals.accel = {form = "p"} table.insert(data.inflections, masculine_plurals) end end }

return export