Jump to content

Module:roa-opt-verb

From Wiktionary, the free dictionary


local export = {}


--[=[

Authorship: Ben Wing <benwing2>

]=]

--[=[

TERMINOLOGY:

-- "slot" = A particular combination of tense/mood/person/number/gender/etc.
	 Example slot names for verbs are "pres_1sg" (present first singular) and
	 "past_pasv_part_impers" (impersonal past passive participle).
	 Each slot is filled with zero or more forms.

-- "form" = The conjugated Old Galician-Portuguese form representing the value of a given slot.

-- "lemma" = The dictionary form of a given Old Galician-Portuguese term. Generally the infinitive,
	 but may occasionally be another form if the infinitive is missing.
]=]

local lang = require("Module:languages").getByCode("roa-opt")
local langname = lang:getCanonicalName()
local m_string_utilities = require("Module:string utilities")
local m_script_utilities = require("Module:script utilities")
local iut = require("Module:inflection utilities")
local put = require("Module:parse utilities")
local m_para = require("Module:parameters")
local m_links = require("Module:links")

local rsplit = mw.text.split
local rfind = mw.ustring.find
local rmatch = mw.ustring.match
local rgmatch = mw.ustring.gmatch
local rsubn = mw.ustring.gsub
local ulen = mw.ustring.len
local uupper = mw.ustring.upper


-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
	local retval = rsubn(term, foo, bar)
	return retval
end


-- version of rsubn() that returns a 2nd argument boolean indicating whether
-- a substitution was made.
local function rsubb(term, foo, bar)
	local retval, nsubs = rsubn(term, foo, bar)
	return retval, nsubs > 0
end


local function tag_text(text)
	return m_script_utilities.tag_text(text, lang)
end


local function make_link(term)
	return m_links.full_link({ lang = lang, term = term }, "term")
end


local verb_slots = {
	{"infinitive", "inf"},
	{"gerund", "ger"},
	{"pp_ms", "past|part"},
	{"pp_mp", "m|p|past|part"},
	{"pp_fs", "f|s|past|part"},
	{"pp_fp", "f|p|past|part"},
	{"presp_s", "pres|part"},
	{"presp_p", "p|pres|part"},
	{"pres_1s", "1|s|pres|ind"},
	{"pres_2s", "2|s|pres|ind"},
	{"pres_3s", "3|s|pres|ind"},
	{"pres_1p", "1|p|pres|ind"},
	{"pres_2p", "2|p|pres|ind"},
	{"pres_3p", "3|p|pres|ind"},
	{"impf_1s", "1|s|impf|ind"},
	{"impf_2s", "2|s|impf|ind"},
	{"impf_3s", "3|s|impf|ind"},
	{"impf_1p", "1|p|impf|ind"},
	{"impf_2p", "2|p|impf|ind"},
	{"impf_3p", "3|p|impf|ind"},
	{"pret_1s", "1|s|pret|ind"},
	{"pret_2s", "2|s|pret|ind"},
	{"pret_3s", "3|s|pret|ind"},
	{"pret_1p", "1|p|pret|ind"},
	{"pret_2p", "2|p|pret|ind"},
	{"pret_3p", "3|p|pret|ind"},
	{"plup_1s", "1|s|plup|ind"},
	{"plup_2s", "2|s|plup|ind"},
	{"plup_3s", "3|s|plup|ind"},
	{"plup_1p", "1|p|plup|ind"},
	{"plup_2p", "2|p|plup|ind"},
	{"plup_3p", "3|p|plup|ind"},
	{"fut_1s", "1|s|fut|ind"},
	{"fut_2s", "2|s|fut|ind"},
	{"fut_3s", "3|s|fut|ind"},
	{"fut_1p", "1|p|fut|ind"},
	{"fut_2p", "2|p|fut|ind"},
	{"fut_3p", "3|p|fut|ind"},
	{"cond_1s", "1|s|cond"},
	{"cond_2s", "2|s|cond"},
	{"cond_3s", "3|s|cond"},
	{"cond_1p", "1|p|cond"},
	{"cond_2p", "2|p|cond"},
	{"cond_3p", "3|p|cond"},
	{"sub_1s", "1|s|pres|sub"},
	{"sub_2s", "2|s|pres|sub"},
	{"sub_3s", "3|s|pres|sub"},
	{"sub_1p", "1|p|pres|sub"},
	{"sub_2p", "2|p|pres|sub"},
	{"sub_3p", "3|p|pres|sub"},
	{"impfsub_1s", "1|s|impf|sub"},
	{"impfsub_2s", "2|s|impf|sub"},
	{"impfsub_3s", "3|s|impf|sub"},
	{"impfsub_1p", "1|p|impf|sub"},
	{"impfsub_2p", "2|p|impf|sub"},
	{"impfsub_3p", "3|p|impf|sub"},
	{"futsub_1s", "1|s|fut|sub"},
	{"futsub_2s", "2|s|fut|sub"},
	{"futsub_3s", "3|s|fut|sub"},
	{"futsub_1p", "1|p|fut|sub"},
	{"futsub_2p", "2|p|fut|sub"},
	{"futsub_3p", "3|p|fut|sub"},
	{"imp_2s", "2|s|imp"},
	{"imp_3s", "3|s|imp"},
	{"imp_1p", "1|p|imp"},
	{"imp_2p", "2|p|imp"},
	{"negimp_2s", "-"},
	{"negimp_3s", "-"},
	{"negimp_1p", "-"},
	{"negimp_2p", "-"},
	{"persinf_1s", "1|s|pers|inf"},
	{"persinf_2s", "2|s|pers|inf"},
	{"persinf_3s", "3|s|pers|inf"},
	{"persinf_1p", "1|p|pers|inf"},
	{"persinf_2p", "2|p|pers|inf"},
	{"persinf_3p", "3|p|pers|inf"},
}


local function fetch_footnotes(separated_group, parse_err)
	local footnotes
	for j = 2, #separated_group - 1, 2 do
		if separated_group[j + 1] ~= "" then
			parse_err("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'")
		end
		if not footnotes then
			footnotes = {}
		end
		table.insert(footnotes, separated_group[j])
	end
	return footnotes
end


local function process_overrides(forms, args)
	for _, slotspec in ipairs(verb_slots) do
		local slot, accel = unpack(slotspec)
		if args[slot] then
			if args[slot] ~= "-" and args[slot] ~= "—" then
				local parse_err = put.make_parse_err(("%s=%s"):format(slot, args[slot]))
				local segments = put.parse_balanced_segment_run(args[slot], "[", "]")
				segments = put.rejoin_delimited_runs {
					runs = segments,
					delimiter_pattern = "^%[%[.*%]%]$"
				}
				local comma_separated_groups = put.split_alternating_runs_and_strip_spaces(segments, ",")
				for _, comma_separated_group in ipairs(comma_separated_groups) do
					local formobj = {}
					formobj.form = comma_separated_group[1]
					formobj.footnotes = fetch_footnotes(comma_separated_group, parse_err)
					iut.insert_form(forms, slot, formobj)
				end
			end
		end
	end
end


local function process_derived_slots(alternant_multiword_spec)
	if not alternant_multiword_spec.noimp then
		local forms = alternant_multiword_spec.forms 
		for _, from_to in ipairs { { "sub_3s", "imp_3s" }, { "sub_1p", "imp_1p" } } do
			local from, to = unpack(from_to)
			if forms[from] then
				iut.insert_forms(forms, to, forms[from])
			end
		end
		for _, from_to in ipairs { { "sub_2s", "negimp_2s" }, { "sub_3s", "negimp_3s" },
			{ "sub_1p", "negimp_1p" }, { "sub_2p", "negimp_2p" } } do
			local from, to = unpack(from_to)
			if forms[from] then
				for _, formobj in ipairs(forms[from]) do
					local form = formobj.form
					local prefix = "[[non]] "
					if form:find("^%*") then
						prefix = "*" .. prefix
						form = form:sub(2)
					end
					if not form:find("%[%[") then
						form = "[[" .. form .. "]]"
					end
					form = prefix .. form
					iut.insert_form(forms, to, {form = form, footnotes = formobj.footnotes})
				end
			end
		end
	end
end


local function process_aux(alternant_multiword_spec)
	local auxparam = alternant_multiword_spec.aux or "haver-teer"
	local haver = make_link("haver")
	local next_note = 0
	local aux_footnotes = {}
	local function insert_and_get_footnote(footnote)
		table.insert(aux_footnotes, footnote)
		next_note = next_note + 1
		return '<sup class="roa-red-superscript">' .. next_note .. "</sup>"
	end
	local aux_text
	if auxparam == "-" then
		aux_text = ""
	else
		local auxvals = rsplit(auxparam, "%-")
		local aux_parts = {}
		local saw_seer = false
		local saw_haver = false
		for _, aux in ipairs(auxvals) do
			if aux == "haver" then
				local note = insert_and_get_footnote("[its alternative spelling, " .. make_link("aver") ..
					", can be used as well]")
				table.insert(aux_parts, haver .. note)
				saw_haver = true
			elseif aux == "teer" then
				local note = insert_and_get_footnote("[" .. make_link("teer") .. " and " .. make_link("ter") ..
				" were used too, though all 3 were less common than forms of " .. haver .. "]")
				table.insert(aux_parts, make_link("tẽer") .. note)
				saw_haver = true
			elseif aux == "seer" then
				table.insert(aux_parts, make_link("seer"))
				saw_seer = true
			else
				error((
					"Unrecognized auxiliary '%s', should be 'haver', 'teer', 'seer' or a hyphen-separated combination"
				):format(aux))
			end
		end
		aux_text = table.concat(aux_parts, " or ")
		if saw_seer or saw_haver then
			table.insert(alternant_multiword_spec.categories, langname .. " verbs taking " .. (
				saw_haver and saw_seer and "haver or seer" or saw_seer and "seer" or "haver") .. " as auxiliary")
		end
	end
	alternant_multiword_spec.aux_text = aux_text
	alternant_multiword_spec.aux_footnotes = aux_footnotes
end


local function show_forms(alternant_multiword_spec)
	local function create_footnote_obj()
		local obj = iut.create_footnote_obj()
		iut.get_footnote_text(alternant_multiword_spec.aux_footnotes, obj)
		return obj
	end

	local function generate_link(data)
		local formobj = data.form
		local form = formobj.form
		local prefix = ""
		if form:find("^%*") then
			prefix = "*"
			form = form:sub(2)
		end
		return prefix .. m_links.full_link { lang = lang, term = form, accel = formobj.accel_obj } ..
			iut.get_footnote_text(formobj.footnotes, data.footnote_obj)
	end

	alternant_multiword_spec.lemmas = alternant_multiword_spec.forms.infinitive or {}
	local props = {
		lemmas = alternant_multiword_spec.lemmas,
		create_footnote_obj = create_footnote_obj,
		generate_link = generate_link,
		slot_list = verb_slots,
		lang = lang,
	}
	iut.show_forms(alternant_multiword_spec.forms, props)
end


local function make_spec(has_aux, has_footnotes)
	return [=[
<div class="NavFrame">
<div class="NavHead">{title}{annotation}</div>
<div class="NavContent">
{\op}| class="roa-inflection-table" data-toggle-category="inflection"
|-
! colspan="2" {rowspan_if_aux} class="roa-nonfinite-header" | infinitive
! colspan="2" class="roa-nonfinite-header" | simple
| colspan="4" | {infinitive}
|-
]=] .. (not has_aux and "" or [=[
! colspan="2" class="roa-nonfinite-header" | compound
! colspan="4" class="roa-compound-row"| infinitive of {aux} + past participle
|-
]=]) .. [=[
! colspan="2" {rowspan_if_aux} class="roa-nonfinite-header" | gerund
! colspan="2" class="roa-nonfinite-header" | simple
| colspan="4" | {gerund}
|-
]=] .. (not has_aux and "" or [=[
! colspan="2" class="roa-nonfinite-header" | compound
! colspan="4" class="roa-compound-row"| gerund of {aux} + past participle
|-
]=]) .. [=[
! rowspan="3" colspan="2" class="roa-nonfinite-header" | past participle
| colspan="2" class="roa-nonfinite-header" |
! colspan="2" class="roa-nonfinite-header" | singular
! colspan="2" class="roa-nonfinite-header" | plural
|-
! colspan="2" class="roa-nonfinite-header" | masculine
| colspan="2" | {pp_ms}
| colspan="2" | {pp_mp}
|-
! colspan="2" class="roa-nonfinite-header" | feminine
| colspan="2" | {pp_fs}
| colspan="2" | {pp_fp}
|-
! colspan="4" class="roa-nonfinite-header" | present participle
| colspan="2" |  {presp_s}
| colspan="2" |  {presp_p}
|-
! colspan="2" rowspan="2" class="roa-person-number-header" | person
! colspan="3" class="roa-person-number-header" | singular
! colspan="3" class="roa-person-number-header" | plural
|-
! class="roa-person-number-header" style="width:12.5%;" | first
! class="roa-person-number-header" style="width:12.5%;" | second
! class="roa-person-number-header" style="width:12.5%;" | third
! class="roa-person-number-header" style="width:12.5%;" | first
! class="roa-person-number-header" style="width:12.5%;" | second
! class="roa-person-number-header" style="width:12.5%;" | third
|-
! class="roa-indicative-left-rail" colspan="2" | indicative mood
! class="roa-indicative-left-rail" | {eu}<br />{ei}
! class="roa-indicative-left-rail" | {tu}
! class="roa-indicative-left-rail" | {el}~{ele}<br />{ela}<br />{vossa_mercee}
! class="roa-indicative-left-rail" | {nos}<br />{nos_outros}<br />{nos_outras}
! class="roa-indicative-left-rail" | {vos}<br />{vos_outros}<br />{vos_outras}
! class="roa-indicative-left-rail" | {eles}<br />{elas}
|-
! rowspan="6" class="roa-indicative-left-rail" | simple<br />tenses
! class="roa-indicative-left-rail" | present
| {pres_1s}
| {pres_2s}
| {pres_3s}
| {pres_1p}
| {pres_2p}
| {pres_3p}
|-
! class="roa-indicative-left-rail" | imperfect
| {impf_1s}
| {impf_2s}
| {impf_3s}
| {impf_1p}
| {impf_2p}
| {impf_3p}
|-
! class="roa-indicative-left-rail" | preterite
| {pret_1s}
| {pret_2s}
| {pret_3s}
| {pret_1p}
| {pret_2p}
| {pret_3p}
|-
! class="roa-indicative-left-rail" | pluperfect
| {plup_1s}
| {plup_2s}
| {plup_3s}
| {plup_1p}
| {plup_2p}
| {plup_3p}
|-
! class="roa-indicative-left-rail" | future
| {fut_1s}
| {fut_2s}
| {fut_3s}
| {fut_1p}
| {fut_2p}
| {fut_3p}
|-
! class="roa-indicative-left-rail" | conditional
| {cond_1s}
| {cond_2s}
| {cond_3s}
| {cond_1p}
| {cond_2p}
| {cond_3p}
|-
]=] .. (not has_aux and "" or [=[
! rowspan="6" class="roa-indicative-left-rail" | compound<br />tenses
! class="roa-indicative-left-rail" | present perfect
! colspan="6" class="roa-compound-row" | present of {aux} + past participle
|-
! class="roa-indicative-left-rail" | present imperfect
! colspan="6" class="roa-compound-row" | imperfect of {aux} + past participle
|-
! class="roa-indicative-left-rail" | past anterior
! colspan="6" class="roa-compound-row" | preterite of {aux} + past participle
|-
! class="roa-indicative-left-rail" | pluperfect
! colspan="6" class="roa-compound-row" | simple pluperfect of {aux} + past participle
|-
! class="roa-indicative-left-rail" | future perfect
! colspan="6" class="roa-compound-row" | future of {aux} + past participle
|-
! class="roa-indicative-left-rail" | conditional perfect
! colspan="6" class="roa-compound-row" | conditional of {aux} + past participle
|-
]=]) .. [=[
! class="roa-subjunctive-left-rail" colspan="2" | subjunctive mood
! class="roa-subjunctive-left-rail" | {eu}<br />{ei}
! class="roa-subjunctive-left-rail" | {tu}
! class="roa-subjunctive-left-rail" | {el}~{ele}<br />{ela}<br />{vossa_mercee}
! class="roa-subjunctive-left-rail" | {nos}<br />{nos_outros}<br />{nos_outras}
! class="roa-subjunctive-left-rail" | {vos}<br />{vos_outros}<br />{vos_outras}
! class="roa-subjunctive-left-rail" | {eles}<br />{elas}
|-
! rowspan="3" class="roa-subjunctive-left-rail" | simple<br />tenses
! class="roa-subjunctive-left-rail" | present
| {sub_1s}
| {sub_2s}
| {sub_3s}
| {sub_1p}
| {sub_2p}
| {sub_3p}
|-
! class="roa-subjunctive-left-rail" | imperfect
| {impfsub_1s}
| {impfsub_2s}
| {impfsub_3s}
| {impfsub_1p}
| {impfsub_2p}
| {impfsub_3p}
|-
! class="roa-subjunctive-left-rail" | future
| {futsub_1s}
| {futsub_2s}
| {futsub_3s}
| {futsub_1p}
| {futsub_2p}
| {futsub_3p}
|-
]=] .. (not has_aux and "" or [=[
! rowspan="3" class="roa-subjunctive-left-rail" | compound<br />tenses
! class="roa-subjunctive-left-rail" | present perfect
! colspan="6" class="roa-compound-row" | present subjunctive of {aux} + past participle
|-
! class="roa-subjunctive-left-rail" | pluperfect
! colspan="6" class="roa-compound-row" | preterite subjunctive of {aux} + past participle
|-
! class="roa-subjunctive-left-rail" | future perfect
! colspan="6" class="roa-compound-row" | future subjunctive of {aux} + past participle
|-
]=]) .. [=[
! colspan="2" class="roa-imperative-left-rail" | imperative mood
! class="roa-imperative-left-rail" | &mdash;
! class="roa-imperative-left-rail" | {tu}
! class="roa-imperative-left-rail" | {vossa_mercee}
! class="roa-imperative-left-rail" | {nos}<br />{nos_outros}<br />{nos_outras}
! class="roa-imperative-left-rail" | {vos}<br />{vos_outros}<br />{vos_outras}
! class="roa-imperative-left-rail" | &mdash;
|-
! colspan="2" class="roa-imperative-left-rail" | affirmative
| —
| {imp_2s}
| {imp_3s}
| {imp_1p}
| {imp_2p}
| —
|-
! colspan="2" class="roa-imperative-left-rail" | negative
| —
| {negimp_2s}
| {negimp_3s}
| {negimp_1p}
| {negimp_2p}
| —
|-
! rowspan="2" colspan="2" class="roa-nonfinite-header" | personal infinitive
! class="roa-nonfinite-header" | {eu}<br />{ei}
! class="roa-nonfinite-header" | {tu}
! class="roa-nonfinite-header" | {el}~{ele}<br />{ela}<br />{vossa_mercee}
! class="roa-nonfinite-header" | {nos}<br />{nos_outros}<br />{nos_outras}
! class="roa-nonfinite-header" | {vos}<br />{vos_outros}<br />{vos_outras}
! class="roa-nonfinite-header" | {eles}<br />{elas}
|-
| {persinf_1s}
| {persinf_2s}
| {persinf_3s}
| {persinf_1p}
| {persinf_2p}
| {persinf_3p}
]=] .. (not has_footnotes and "" or [=[
|-
| colspan="10" align="left"|{footnote}
]=]) .. [=[|{\cl}</div></div>]=]
end


local function make_table(alternant_multiword_spec)
	local forms = alternant_multiword_spec.forms

	if alternant_multiword_spec.title then
		forms.title = alternant_multiword_spec.title
	else
		local lemmaspec
		if alternant_multiword_spec.lemmas[1] then
			local lemmas = {}
			for _, inf in ipairs(alternant_multiword_spec.lemmas) do
				table.insert(lemmas, inf.form)
			end
			lemmaspec = table.concat(lemmas, ", ")
		else
			lemmaspec = "—"
		end
		forms.title = 'Conjugation of <i lang="roa-opt" class="Latn">' .. lemmaspec .. '</i>'
	end

	local ann_parts = {}
	if alternant_multiword_spec.impers then
		table.insert(ann_parts, "impersonal")
	end
	if alternant_multiword_spec.irreg then
		table.insert(ann_parts, "irregular")
	end
	if ann_parts[1] then
		forms.annotation = " (" .. table.concat(ann_parts, ", ") .. ")"
	else
		forms.annotation = ""
	end

	-- pronouns used in the table
	forms.eu = tag_text("eu")
	forms.ei = tag_text("ei")
	forms.tu = tag_text("tu")
	forms.el = tag_text("el")
	forms.ele = tag_text("ele")
	forms.ela = tag_text("ela")
	forms.vossa_mercee = tag_text(alternant_multiword_spec.impers and "<s>vossa mercee</s>" or "vossa mercee")
	forms.nos = tag_text("nos")
	forms.nos_outros = tag_text("nos outros")
	forms.nos_outras = tag_text("nos outras")
	forms.vos = tag_text("vos")
	forms.vos_outros = tag_text("vos outros")
	forms.vos_outras = tag_text("vos outras")
	forms.eles = tag_text("eles")
	forms.elas = tag_text("elas")

	local has_footnote = forms.footnote ~= ""
	local has_aux = alternant_multiword_spec.aux_text ~= ""
	local table_spec = make_spec(has_aux, has_footnote)
	forms.rowspan_if_aux = has_aux and 'rowspan="2"' or ""
	forms.aux = alternant_multiword_spec.aux_text
	return require("Module:TemplateStyles")("Module:roa-verb/style.css") .. m_string_utilities.format(table_spec, forms)
end


-- Externally callable function to parse and conjugate a verb given user-specified arguments. Return value is
-- ALTERNANT_MULTIWORD_SPEC, an object where the conjugated forms are in `ALTERNANT_MULTIWORD_SPEC.forms`
-- for each slot. If there are no values for a slot, the slot key will be missing. The value for a given slot
-- is a list of objects {form=FORM, footnotes=FOOTNOTES}.
function export.do_generate_forms_manual(parent_args)
	local params = {
		aux = {},
		title = {},
		irreg = {type = "boolean"},
		impers = {type = "boolean"},
		noimp = {type = "boolean"},
	}
	for _, slotspec in ipairs(verb_slots) do
		local slot, accel = unpack(slotspec)
		params[slot] = {}
	end

	local args = m_para.process(parent_args, params)
	local alternant_multiword_spec = {
		forms = {},
		aux = args.aux,
		title = args.title,
		irreg = args.irreg,
		impers = args.impers,
		noimp = args.noimp,
		categories = {},
	}
	if args.impers then
		table.insert(alternant_multiword_spec.categories, langname .. " impersonal verbs")
	end
	if args.irreg then
		table.insert(alternant_multiword_spec.categories, langname .. " irregular verbs")
	end
	process_overrides(alternant_multiword_spec.forms, args)
	process_derived_slots(alternant_multiword_spec)
	process_aux(alternant_multiword_spec)
	return alternant_multiword_spec
end


-- Entry point for {{roa-opt-conj-manual}}. Template-callable function to parse and conjugate a verb given
-- manually-specified inflections and generate a displayable table of the conjugated forms.
function export.show_manual(frame)
	local parent_args = frame:getParent().args
	local alternant_multiword_spec = export.do_generate_forms_manual(parent_args)
	show_forms(alternant_multiword_spec)
	return make_table(alternant_multiword_spec) .. require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang)
end


return export