Module:qhv-headword

Revision as of 15:23, 27 March 2021 by Sware (talk | contribs)


local export = {}
local pos_functions = {}

local sub = mw.ustring.sub
local find = mw.ustring.find
local match = mw.ustring.match
local gmatch = mw.ustring.gmatch
local gsub = mw.ustring.gsub
local u = mw.ustring.char
local split = mw.text.split
local gsplit = mw.text.gsplit

local PAGENAME = mw.title.getCurrentTitle().text
local NAMESPACE = mw.title.getCurrentTitle().nsText
local SUBPAGENAME = mw.title.getCurrentTitle().subpageText

local legal_gender = {
	["aq"] = true,
	["lun"] = true,
	["sol"] = true,
	["ter"] = true,
	["?"] = true,
}

local gender_names = {
	["aq"] = "aquatic",
	["lun"] = "lunar",
	["sol"] = "solar",
	["ter"] = "terrestrial",
	["?"] = "unknown",
}

local legal_verb_classes = { -- TBD
	["1"] = true,
	["2"] = true,
	["3"] = true,
	["4"] = true,
	["5"] = true,
	["6"] = true,
	["7"] = true,
}

local lang = require("Module:languages").getByCode("qhv")
local m_data = require("Module:qhv-noun/data")

local function glossary_link(entry, text)
	text = text or entry
	return "[[wikt:Appendix:Glossary#" .. entry .. "|" .. text .. "]]"
end

local function detect_gender(word)
	local ending = sub(word, -1)
	if find(word, "[aeioy]$") then
		return "lun"
	elseif ending == "r" then
		return "aq"
	elseif ending == "s" then
		return "sol"
	elseif ending == "n" then
		return "ter"
	else return "?" end
end

local function generate_gen_sg(word, rc, rp)
	local genitive = {}
	local r = rc and "gen_co"
	for alt in gmatch(require("Module:qhv-noun/head").pass_to_module((rc and "gen_co" or rp and "gen_pa" or "gen_sg"), word), "[^,]+") do
		table.insert(genitive, alt)
	end
	return genitive
end

local function generate_nom_pl(word)
	local plural = {}
	for alt in gmatch(require("Module:qhv-noun/head").pass_to_module("nom_pl", word), "[^,]+") do
		table.insert(plural, alt)
	end
	return plural
end

-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
	if NAMESPACE == "Template" and SUBPAGENAME ~= "doc" then return end
	local parent_args = frame:getParent().args
	
	local head = parent_args["head"]; if head == "" then head = nil end
	
	local poscat = frame.args[1] or error("Part of speech has not been specified. Please pass parameter 1 to the module invocation.")
	local class = frame.args[2]; if class == "" then class = nil end
	
	local data = {
		lang = lang,
		heads = {head},
		inflections = {},
		genders = {},
		pos_category = poscat,
		categories = {"Contionary"},
	}
	
	if pos_functions[poscat] then
		pos_functions[poscat](class, parent_args, data)
	end
	
	return require("Module:headword").full_headword(data)
end

pos_functions.nouns = function(class, args, data)
	local params = {
		[1] = {default = detect_gender(PAGENAME)},
		[2] = {default = PAGENAME},
		["nopl"] = {},
		["noinf"] = {type = "boolean", default = false},
		["gen"] = {},
		["pl"] = {},
		["head"] = {},
		["rc"] = {type = "boolean"},
		["rp"] = {type = "boolean"},
		["og"] = {},
	}
	
	local args = require("Module:parameters").process(args, params)
	data.heads = {args["head"]}
	
	table.insert(data.genders, args[1])
	table.insert(data.categories, "High Valyrian " .. (args[1] == "?" and "" or gender_names[args[1]]) .. " " .. data.pos_category)
	
	if not args["noinf"] then
		args["gen"] = generate_gen_sg(args["og"] or args[2], args["rc"], args["rp"])
	
		for i, form in ipairs(args["gen"]) do
			args["gen"][i] = {term = form}
		end
		
		args["gen"].label = "genitive"
		table.insert(data.inflections, args["gen"])
	end
		
	if data.pos_category == "nouns" and not args["noinf"] then
		if not args["nopl"] then
			args["pl"] = generate_nom_pl(args["og"] or args[2])
			
			for i, form in ipairs(args["pl"]) do
				args["pl"][i] = {term = form}
			end
			
			args["pl"].label = "plural"
			table.insert(data.inflections, args["pl"])
		end
	end
end

pos_functions.adjectives = function(class, args, data)
	local params = {
		[1] = {list = "eq"},
		[2] = {list = "comp"},
		[3] = {list = "sup"},
		["unc"] = {type = "boolean"},
		["indecl"] = {type = "boolean"},
		["head"] = {},
	}
	
	local args = require("Module:parameters").process(args, params)
	local isdet = data.pos_category == "determiners"
	data.heads = {args["head"]}
	
	table.insert(data.categories, "High Valyrian " .. data.pos_category)
	if args["unc"] or isdet then
		table.insert(data.inflections, {label = "not " .. glossary_link("comparable")})
		if not isdet then table.insert(data.categories, "High Valyrian uncomparable adjectives") end
	else
		args[1] = require("Module:qhv-adj/head").fetch("eq")
		args[2] = require("Module:qhv-adj/head").fetch("comp")
		args[3] = require("Module:qhv-adj/head").fetch("sup")
		
		if args["indecl"] then
			table.insert(data.inflections, {label = glossary_link("indeclinable")})
			table.insert(data.categories, "High Valyrian indeclinable " .. data.pos_category)
		end
		args[1].label = "[[wikt:Appendix:Glossary#equative|equative]]"
		args[2].label = "[[wikt:Appendix:Glossary#comparative|comparative]]"
		args[3].label = "[[wikt:Appendix:Glossary#suoerlative|superlative]]"
		
		table.insert(data.inflections, args[1])
		table.insert(data.inflections, args[2])
		table.insert(data.inflections, args[3])
	end
end

pos_functions["proper nouns"] = pos_functions.nouns
pos_functions["determiners"] = pos_functions.adjectives

pos_functions.verbs = function(class, args, data)
	if args[2] then -- old-style
		local params = {
			[1] = {list = "pres", required = true},
			["pres_qual"] = {list = "pres=_qual", allow_holes = true},
			[2] = {list = "past", required = true},
			["past_qual"] = {list = "past=_qual", allow_holes = true},
			[3] = {list = "pp", required = true},
			["pp_qual"] = {list = "pp=_qual", allow_holes = true},
			[4] = {list = "pastsubj"},
			["pastsubj_qual"] = {list = "pastsubj=_qual", allow_holes = true},
			["aux"] = {list = true},
			["aux_qual"] = {list = "aux=_qual", allow_holes = true},
			["head"] = {list = true},
			["class"] = {list = true},
		}

		local args = require("Module:parameters").process(args, params)
		data.heads = args["head"]

		local function collect_forms(label, accel_form, forms, qualifiers)
			if forms[1] == "-" then
				return {label = "no " .. label}
			else
				local into_table = accel_form and {label = label, accel = {form = accel_form}} or {label = label}
				for i, form in ipairs(forms) do
					table.insert(into_table, {term = form, qualifiers = qualifiers[i] and {qualifiers[i]} or nil})
				end
				return into_table
			end
		end

		if #args.class > 0 then
			local class_descs, cats = require("Module:de-verb").process_verb_classes(args.class)
			for _, cats in ipairs(cats) do
				table.insert(data.categories, cats)
			end
			table.insert(data.inflections, {label = require("Module:table").serialCommaJoin(class_descs, {conj = "or"})})
		end
		table.insert(data.inflections, collect_forms("third-person singular present", "3|s|pres", args[1], args.pres_qual))
		table.insert(data.inflections, collect_forms("past tense", "1//3|s|pret", args[2], args.past_qual))
		table.insert(data.inflections, collect_forms("past participle", "perf|part", args[3], args.pp_qual))
		if #args[4] > 0 then
			table.insert(data.inflections, collect_forms("past subjunctive", "1//3|s|sub|II", args[4], args.pastsubj_qual))
		end
		if #args.aux > 0 then
			table.insert(data.inflections, collect_forms("auxiliary", nil, args.aux, args.aux_qual))
		end
		return
	end

	local function get_headword_inflection(forms, label, accel_form)
		if forms then
			local inflection = accel_form and {label = label, accel = {form = accel_form}} or {label = label}
			for _, form in ipairs(forms) do
				local qualifiers
				if form.footnotes then
					qualifiers = {}
					for _, footnote in ipairs(form.footnotes) do
						footnote = footnote:gsub("^%[(.*)%]$", "%1")
						table.insert(qualifiers, footnote)
					end
				end
				table.insert(inflection, {term = form.form, qualifiers = qualifiers})
			end
			return inflection
		elseif label then
			return {label = "no " .. label}
		else
			return {}
		end
	end

	local alternant_multiword_spec = require("Module:de-verb").do_generate_forms(args, "from headword")
	for _, cat in ipairs(alternant_multiword_spec.categories) do
		table.insert(data.categories, cat)
	end
	table.insert(data.inflections, {label = table.concat(alternant_multiword_spec.verb_types, " or ")})

	if #data.heads == 0 then
		for _, head in ipairs(alternant_multiword_spec.forms.infinitive_linked) do
			table.insert(data.heads, head.form)
		end
	end
	table.insert(data.inflections, get_headword_inflection(alternant_multiword_spec.forms.pres_3s,
		"third-person singular present", "3|s|pres"))
	local pret_3s = alternant_multiword_spec.forms.pret_3s
	table.insert(data.inflections, get_headword_inflection(pret_3s, "past tense", "1//3|s|pret"))
	table.insert(data.inflections, get_headword_inflection(alternant_multiword_spec.forms.perf_part,
		"past participle", "perf|part"))
	-- See if we need the past subjunctive, i.e. there exist past subjunctive forms whose stem is not the
	-- same as some past tense form. To facilitate comparison, we truncate final -e in both preterite 3s
	-- and past subjunctive 3s, to handle cases like subjunctive 'ginge aus' vs. preterite 'ging aus'.
	-- We need to compare 3s forms (and not e.g. 3p forms, where the issue with truncating -e doesn't
	-- occur) so we work correctly with impersonal verbs.
	local need_past_subj
	local truncated_pret_3s_forms = {}
	if pret_3s then
		for _, form in ipairs(pret_3s) do
			local truncated_form = form.form:gsub("e$", ""):gsub("e ", " ") -- discard 2nd retval
			table.insert(truncated_pret_3s_forms, truncated_form)
		end
	end
	local subii_3s = alternant_multiword_spec.forms.subii_3s
	local truncated_subii_3s_forms = {}
	if subii_3s then
		for _, form in ipairs(subii_3s) do
			local truncated_form = form.form:gsub("e$", ""):gsub("e ", " ") -- discard 2nd retval
			table.insert(truncated_subii_3s_forms, truncated_form)
		end
	end
	for _, past_subj_form in ipairs(truncated_subii_3s_forms) do
		local saw_same = false
		for _, pret_3s_form in ipairs(truncated_pret_3s_forms) do
			if past_subj_form == pret_3s_form then
				saw_same = true
				break
			end
		end
		if not saw_same then
			need_past_subj = true
			break
		end
	end
	if need_past_subj then
		table.insert(data.inflections, get_headword_inflection(subii_3s, "past subjunctive", "1//3|s|sub|II"))
	end

	local auxes = alternant_multiword_spec.forms.aux
	table.insert(data.inflections, get_headword_inflection(auxes, "auxiliary"))
end

return export