ข้ามไปเนื้อหา

มอดูล:la-headword

จาก วิกิพจนานุกรม พจนานุกรมเสรี

-- TODO: handle "-old" subtypes generally, appending "in Old Latin", where terms have additional OL inflections.

local export = {}
local pos_functions = {}

local string_utilities = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"

local concat = table.concat
local form_is_empty = require("Module:la-utilities").form_is_empty
local insert = table.insert
local remove = table.remove
local umatch = mw.ustring.match

local lang = require("Module:languages").getByCode("la")
local NAMESPACE = mw.title.getCurrentTitle().namespace
local PAGENAME = mw.loadData("Module:headword/data").pagename

local legal_gender = {
	["m"] = true,
	["f"] = true,
	["n"] = true,
	["?"] = true,
	["?!"] = true,
}

local declension_to_thai = {
	["1"] = "ที่ 1",
	["2"] = "ที่ 2",
	["3"] = "ที่ 3",
	["4"] = "ที่ 4",
	["5"] = "ที่ 5",
}

local gender_names = {
	["m"] = "เพศชาย",
	["f"] = "เพศหญิง",
	["n"] = "เพศกลาง",
	["?"] = "ไม่ทราบเพศ",
	["?!"] = "unattested gender",
}

local function deep_equals(...)
	deep_equals = require(table_module).deepEquals
	return deep_equals(...)
end

local function get_plaintext(...)
	get_plaintext = require(utilities_module).get_plaintext
	return get_plaintext(...)
end

local function insert_if_not(...)
	insert_if_not = require(table_module).insertIfNot
	return insert_if_not(...)
end

local function invert(...)
	invert = require(table_module).invert
	return invert(...)
end

local function keys_to_list(...)
	keys_to_list = require(table_module).keysToList
	return keys_to_list(...)
end

local function pattern_escape(...)
	pattern_escape = require(string_utilities).pattern_escape
	return pattern_escape(...)
end

local function serial_comma_join(...)
	serial_comma_join = require(table_module).serialCommaJoin
	return serial_comma_join(...)
end

local function split(...)
	split = require(string_utilities).split
	return split(...)
end

local function ulower(...)
	ulower = require(string_utilities).lower
	return ulower(...)
end

local function format(array, concatenater)
	if #array == 0 then
		return ""
	end
	local concatenated = concat(array, concatenater)
	if concatenated == "" then
		return ""
	elseif concatenated:sub(-1) == "'" then
		concatenated = concatenated .. " "
	end
	return "; ''" .. concatenated .. "''"
end

local function glossary_link(anchor, text)
	text = text or anchor
	return "[[ภาคผนวก:อภิธานศัพท์#" .. anchor .. "|" .. text .. "]]"
end

local function make_link(page, display, face, accel)
	return require("Module:links").full_link({term = page, alt = display, lang = lang, accel = accel}, face)
end

-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
	local iargs = require("Module:parameters").process(frame.args, {
		[1] = {required = true},
		["def"] = true,
		["suff_type"] = true,
	})
	local args = frame:getParent().args
	local poscat = iargs[1]
	-- หมวดหมู่เป็นภาษาไทย
	local poscat_th = require("Module:th-utilities").th_pos(poscat)
	local def = iargs.def
	local suff_type = iargs.suff_type
	local postype = nil
	if suff_type then
		postype = 'รูป' .. poscat_th .. 'เป็น' .. suff_type
	else
		postype = poscat_th
	end

	local data = {lang = lang, categories = {}, heads = {}, genders = {}, inflections = {}}
	local infl_classes = {}
	local title = {}
	local postscript = {}

	if poscat_th == "ปัจจัย" then
		insert_if_not(data.categories, "ปัจจัยสร้าง" .. suff_type .. "ภาษาละติน")
	end

	if pos_functions[postype] then
		local new_poscat = pos_functions[postype](def, args, data, infl_classes, title, postscript)
		if new_poscat then
			poscat_th = new_poscat
		end
	end

	if NAMESPACE == 118 then -- Reconstruction:
		data.pos_category = poscat_th .. "ที่สืบสร้าง"
		data.nolink = true
	else
		data.pos_category = poscat_th
	end

	postscript = concat(postscript, ", ")

	return
		require("Module:headword").full_headword(data)
		.. format(infl_classes, "/")
		.. format(title, ", ")
		.. (postscript ~= "" and " (" .. postscript .. ")" or "")
end

local function process_num_type(numtype, categories)
	if numtype == "card" then
		insert_if_not(categories, "Latin cardinal numbers")
	elseif numtype == "ord" then
		insert_if_not(categories, "Latin ordinal numbers")
	elseif numtype == "frac" then
		insert_if_not(categories, "Latin fractional numbers")
	elseif numtype == "dist" then
		insert_if_not(categories, "Latin distributive numbers")
	elseif numtype == "mul" then
		insert_if_not(categories, "Latin multiplicative numbers")
	elseif numtype == "coll" then
		insert_if_not(categories, "Latin collective numbers")
	elseif numtype then
		error("Unrecognized numeral type '" .. numtype .. "'")
	end
end

local function normalize_derivation(deriv, deriv_label)
	if not deriv then
		return nil
	end
	local deriv1 = deriv[1]
	if not deriv1 or deriv1 == "-" then
		return deriv1
	end
	deriv.label = glossary_link(deriv_label)
	return deriv
end

local function nouns(pos, def, args, data, infl_classes, title)
	local is_num = pos == "เลข"
	local is_pn = false
	if pos == "คำวิสามานยนาม" then
		is_pn = true
		pos = "คำนาม"
	end
	local decldata = require("Module:la-nominal").do_generate_noun_forms(args, pos, "headword", def, is_num)
	local lemma = decldata.overriding_lemma
	-- TODO: modify the headword line if the slot isn't the expected one.
	if #lemma == 0 then
		for slot in require("Module:la-nominal").iter_potential_noun_lemma_slots() do
			local potential_lemma = decldata.forms["linked_" .. slot]
			if not form_is_empty(potential_lemma) then
				if decldata.unattested[slot] then
					potential_lemma = "*" .. potential_lemma
				end
				lemma = potential_lemma
				break
			end
		end
	end

	data.heads = lemma
	-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
	-- really any redundant heads.
	data.no_redundant_head_cat = true
	data.id = decldata.id
	
	local genders = decldata.overriding_genders
	if #genders == 0 then
		if decldata.gender then
			genders = {ulower(decldata.gender)}
		elseif not is_num then
			error("No gender explicitly specified in headword template using g=, and can't infer gender from lemma spec")
		end
	end

	local categories = data.categories
	if is_num then
		process_num_type(decldata.num_type, categories)
	end

	if decldata.indecl then
		insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
		insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")

		for _, g in ipairs(genders) do
			local gender = g:match("^(.[\128-\191]*)%-[sp]$")
			if not gender then
				gender = g
			end
			if not legal_gender[gender] then
				error("Gender “" .. gender .. "” is not a valid Latin gender.")
			end
			insert(data.genders, g)
			insert_if_not(categories, decldata.pos .. gender_names[gender] .. "ผันรูปไม่ได้ภาษาละติน")
		end
	else
		local is_irreg = false
		local is_indecl = false
		local is_decl = false
		local has_multiple_decls = false
		local has_multiple_variants = false
		-- flatten declension specs
		local decls = {}

		for _, g in ipairs(genders) do
			if not legal_gender[g] then
				error("Gender “" .. g .. "” is not a valid Latin gender.")
			elseif decldata.num == "pl" then
				g = g .. "-p"
			elseif decldata.num == "sg" then
				g = g .. "-s"
			end
			insert(data.genders, g)
		end

		local function process_decl(decl_list, props)
			local headword_decl = props.headword_decl
			-- skip adjectival declensions
			if headword_decl:match("+$") then
				return
			elseif props.decl == "irreg" then
				is_irreg = true
				headword_decl = headword_decl:match("^irreg/(.*)$") or headword_decl
				local irreg_decls = split(headword_decl, ",")
				if #irreg_decls > 1 then
					has_multiple_decls = true
				end
				for _, d in ipairs(irreg_decls) do
					if d == "indecl" or d == "0" then
						is_indecl = true
					else
						is_decl = true
					end
					insert_if_not(decl_list, d)
				end
			else
				if headword_decl == "indecl" or headword_decl == "0" then
					is_indecl = true
				else
					is_decl = true
				end
				insert_if_not(decl_list, headword_decl)
			end
		end

		for _, props in ipairs(decldata.propses) do
			if props.headword_decl then
				process_decl(decls, props)
			else
				local alternant_decls = {}
				for _, alternant in ipairs(props) do
					for _, single_props in ipairs(alternant) do
						process_decl(alternant_decls, single_props)
					end
				end
				if #alternant_decls > 1 then
					has_multiple_decls = true
				elseif #decls > 1 then
					has_multiple_variants = true
				end
				for _, d in ipairs(alternant_decls) do
					insert_if_not(decls, d)
				end
			end
		end

		if is_indecl and is_decl then
			has_multiple_decls = true
		end
		if has_multiple_decls then
			insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple declensions")
		end
		if has_multiple_variants then
			insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple variants of a single declension")
		end
		if is_irreg then
			insert(title, glossary_link("ไม่ปรกติ"))
			insert_if_not(categories, decldata.pos .. "ไม่ปรกติภาษาละติน")
			for _, g in ipairs(genders) do
				insert_if_not(categories, decldata.pos .. gender_names[g] .. "ไม่ปรกติภาษาละติน")
			end
		end

		if is_indecl then
			if is_decl then
				insert(title, glossary_link("ผันรูปไม่ได้"))
			else
				insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
			end
			insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
			for _, g in ipairs(genders) do
				insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูปไม่ได้ภาษาละติน")
			end
		end

		if #decls > 1 then
			insert(title, "ผันรูปหลายแบบ")
		end

		for _, decl in ipairs(decls) do
			if not (decl == "0" or decl == "indecl" or decl == "sgpl" or decl == "irreg") then
				local decl_class = declension_to_thai[decl]
				if not decl_class then
					error("Internal error: declension '" .. decl .. "' not recognized")
				end
				insert(title, "[[ภาคผนวก:การผันรูป" .. decl_class .. " ของคำนามภาษาละติน|การผันรูป" .. decl_class .. "]]")
				insert_if_not(categories, decldata.pos .. "ผันรูป" .. decl_class .. " ภาษาละติน")
				for _, g in ipairs(genders) do
					insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูป" .. decl_class .. " ภาษาละติน")
				end
			end
		end

		local lemma_num = decldata.num == "pl" and "pl" or "sg"
		if NAMESPACE == 118 then -- Reconstruction:
			-- For reconstructed nouns:
			if data.genders[1] == 'n' and lemma_num == 'sg' then
				-- singular neuter nouns give a plural
				local pl = decldata.forms["nom_pl"]
				if pl and pl ~= "" and #pl > 0 then
					pl.label = "plural"
					insert(data.inflections, pl)
				end
			else
				-- all others give an oblique
				local obl = decldata.forms["acc_" .. lemma_num]
				if obl and obl ~= "" and #obl > 0 then
					obl.label = "oblique"
					insert(data.inflections, obl)
				end
			end
		else
			local gen = decldata.forms["gen_" .. lemma_num]
			if (decldata.unattested["gen_" .. lemma_num]) then
				gen[1] = '*' .. gen[1]
				data.nolink = true
			end
			if gen and gen ~= "" and gen ~= "—" and #gen > 0 then
				if is_decl then
					-- Skip displaying the genitive for nouns that are only
					-- indeclinable. But we do display it for nouns like Abrahām
					-- and Ādām that can be either indeclinable or declined.
					gen.label = "สัมพันธการก"
					insert(data.inflections, gen)
				end
			else
				insert(data.inflections, {label = "ไม่มีสัมพันธการก"})
				insert_if_not(categories, "Latin " .. decldata.pos .. " with no genitive singular")
			end
		end
	end

	local m = normalize_derivation(decldata.m, "เพศชาย")
	if m and m ~= "-" then
		insert(data.inflections, m)
	end

	local f = normalize_derivation(decldata.f, "เพศหญิง")
	if f and f ~= "-" then
		insert(data.inflections, f)
	end

	for _, cat in ipairs(decldata.categories) do
		insert_if_not(categories, cat)
	end

	for _, cat in ipairs(decldata.cat) do
		insert_if_not(categories, cat .. "ภาษาละติน")
	end
	
	return is_pn and decldata.pos == "คำนาม" and "คำวิสามานยนาม" or decldata.pos
end

pos_functions["คำนาม"] = function(def, args, data, infl_classes, title)
	return nouns("คำนาม", def, args, data, infl_classes, title)
end

pos_functions["คำวิสามานยนาม"] = function(def, args, data, infl_classes, title)
	return nouns("คำวิสามานยนาม", def, args, data, infl_classes, title)
end

pos_functions["รูปปัจจัยเป็นนาม"] = function(def, args, data, infl_classes, title)
	return nouns("ปัจจัย", def, args, data, infl_classes, title)
end

pos_functions["รูปเลขเป็นนาม"] = function(def, args, data, infl_classes, title)
	return nouns("เลข", def, args, data, infl_classes, title)
end

function export.verb_title(title, typeinfo, lemma_forms)
	local conj = typeinfo.conj
	local irreg_processing = typeinfo.irreg
	local subtypes = typeinfo.subtypes

	local first_lemma = ""
	if #lemma_forms > 0 then
		first_lemma = require("Module:links").remove_links(lemma_forms[1])
	end

	if conj == "1st" then
		insert(title, "[[ภาคผนวก:Latin first conjugation|first conjugation]]")
	elseif conj == "2nd" then
		insert(title, "[[ภาคผนวก:Latin second conjugation|second conjugation]]")
	elseif conj == "3rd" then
		insert(title, "[[ภาคผนวก:Latin third conjugation|third conjugation]]")
	elseif conj == "3rd-io" then
		insert(title, ("[[ภาคผนวก:Latin third conjugation|third conjugation]] %s-variant"):format(make_link(nil, "iō", "term")))
	elseif conj == "4th" then
		insert(title, "[[ภาคผนวก:Latin fourth conjugation|fourth conjugation]]")
	elseif conj == "irreg" then -- sum
		insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular conjugation]]")
	end
	if subtypes.highlydef then -- āiō, inquam
		insert(title, "highly [[defective verb#English|defective]]")
	end
	if subtypes.suppl then -- sum, volō
		insert(title, "[[suppletive#English|suppletive]]")
	end
	if subtypes["3only"] then -- decet
		insert(title, "[[third person#English|third person]]-only")
	elseif subtypes.impers then -- decet, advesperāscit (also nopass)
		insert(title, "[[impersonal verb#English|impersonal]]")
	end
	if subtypes.depon then -- dēmōlior, calvor (also noperf)
		insert(title, "[[deponent#English|deponent]]")
	end
	if subtypes.perfaspres then -- meminī, ōdī
		insert(title, "no [[present tense#English|present]] stem")
		-- If semidepon is set, only the active forms are affected.
		local voice = subtypes.semidepon and "[[active voice#English|active]] " or ""
		insert(title, ("[[perfect tense#English|perfect]] %sforms have [[present tense#English|present]] %smeaning"):format(voice, voice))
		-- Clarify that deponency can only apply to the perfect.
		if subtypes.semidepon then -- ōdī
			insert(title, "[[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
		end
		if subtypes.optsemidepon then
			insert(title, "optionally [[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
		end
	else
		if subtypes.semidepon then -- fīdō, gaudeō
			insert(title, "[[semi-deponent#English|semi-deponent]]")
		end
		if subtypes.optsemidepon then -- audeō, placeō, soleō, pudeō
			insert(title, "optionally [[semi-deponent#English|semi-deponent]]")
		end
	end
	if subtypes.imponly then -- cedo, apage
		insert(title, "[[imperative mood#English|imperative]]-only")
	end
	if subtypes.nopass then -- coacēscō
		insert(title, "no [[passive voice#English|passive]]")
	end
	
	local stems = {}
	if subtypes.nopres then -- coepī; perfaspres already handles this above
		insert(stems, "[[present tense#English|present]]")
	end
	if subtypes.noperf then
		insert(stems, "[[perfect tense#English|perfect]]")
	end
	if subtypes.nosup or subtypes.supfutractvonly then
		insert(stems, "[[supine#English|supine]]")
	end
	if #stems > 0 then
		local extra = subtypes.supfutractvonly and " except in the [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]"
		insert(title, ("no %s stem%s%s"):format(serial_comma_join(stems, {conj = "or"}), #stems > 1 and "s" or "", extra or ""))
	end
	if subtypes.nofut then -- soleō
		insert(title, "no [[future tense#English|future]]")
	end
	if subtypes.pass3only then -- praefundō
		insert(title, "[[third person#English|third person]]-only in the [[passive voice#English|passive]]")
	elseif subtypes.passimpers then -- abambulō
		local msg = "[[impersonal verb#English|impersonal]] in the [[passive voice#English|passive]]"
		if subtypes.passimpersold then -- possum
			msg = msg .. " in [[Old Latin]]"
		end
		insert(title, msg)
	end
	if subtypes.noimp then -- volō
		insert(title, "no [[imperative mood#English|imperative]]")
	end
	if irreg_processing and umatch(first_lemma, "d[īū]cō$") then -- dīcō
		insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] short [[imperative mood#English|imperative]]")
	end
	if subtypes.nofutractvptc and not subtypes.nosup then -- fīō
		insert(title, "no [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]")
	end
	if not (subtypes.nopres or subtypes.perfaspres or subtypes.imponly) then
		if subtypes.noinf then -- inquam
			insert(title, "no [[infinitive#English|infinitive]]")
		end
		if subtypes.noger then -- libet
			insert(title, "no [[gerund#English|gerund]]")
		end
	end
	if subtypes.shorta then -- dō
		insert(title, ("[[ภาคผนวก:Latin irregular verbs|irregular]] short %s in most forms"):format(make_link(nil, "ă", "term")))
	end
	if irreg_processing then
		if first_lemma:match("edō$") then -- edō
			insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] alternative forms")
		elseif first_lemma:match("fīō$") then -- fīō
			insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] long " .. make_link(nil, "ī", "term"))
		end
	end
end

pos_functions["คำกริยา"] = function(def, args, data, infl_classes, title)
	local m_la_verb = require("Module:la-verb")
	local def1, def2
	if def then
		def1, def2 = def:match("^(.-):(.*)$")
	end
	local conjdata, typeinfo = m_la_verb.make_data(args, true, def1, def2)
	local lemma_forms = conjdata.overriding_lemma
	if not lemma_forms or #lemma_forms == 0 then
		lemma_forms = m_la_verb.get_lemma_forms(conjdata, true)
	end
	data.heads = lemma_forms
	-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-verb}}, there aren't
	-- really any redundant heads.
	data.no_redundant_head_cat = true
	data.id = conjdata.id
	
	local perf_only = false

	local function insert_inflection(infl, label)
		infl.label = label
		insert(data.inflections, infl)
	end

	local inf = m_la_verb.get_valid_forms(conjdata.forms["pres_actv_inf"])
	if #inf > 0 then
		insert_inflection(inf, "present infinitive")
	else
		inf = m_la_verb.get_valid_forms(conjdata.forms["perf_actv_inf"])
		if #inf > 0 then
			perf_only = true
			insert_inflection(inf, "perfect infinitive")
		end
	end

	if not perf_only then
		local perf = m_la_verb.get_valid_forms(conjdata.forms["1s_perf_actv_indc"])
		if #perf == 0 then
			perf = m_la_verb.get_valid_forms(conjdata.forms["3s_perf_actv_indc"])
		end
		if #perf > 0 then
			insert_inflection(perf, "perfect active")
		end
	end

	local subtypes = typeinfo.subtypes
	if not (subtypes.depon or subtypes.semidepon) then
		local sup = m_la_verb.get_valid_forms(conjdata.forms["acc_sup"])
		if #sup > 0 then
			insert_inflection(sup, "supine")
		else
			local fap = m_la_verb.get_valid_forms(conjdata.forms["futr_actv_ptc"])
			if #fap > 0 then
				insert_inflection(fap, "future active participle")
			end
		end
	end

	export.verb_title(title, typeinfo, lemma_forms)
end

pos_functions["รูปปัจจัยเป็นกริยา"] = pos_functions["คำกริยา"]

local function attested_form(decldata, index)
	local form
	if (decldata.unattested[index]) then
		form = { { term = '*' .. decldata.forms[index][1], nolink = true } }
	else
		form = decldata.forms[index]
	end
	return form
end

local function degree_derivations(comp, sup, data)
	local inflections = data.inflections
	if not (comp or sup) then
		return
	elseif (not comp or comp == "-") and (not sup or sup == "-") then
		insert(inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|เปรียบเทียบ]]ไม่ได้"})
		insert_if_not(data.categories, "Latin uncomparable adverbs")
		return
	elseif comp == "-" then
		insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|ขั้นกว่า]]"})
	elseif comp then
		insert(inflections, comp)
	end
	if sup == "-" then
		insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นสุด|ขั้นสุด]]"})
	elseif sup then
		insert(inflections, sup)
	end
end

local function adjectives(pos, def, args, data, infl_classes)
	local is_num = pos == "เลข"
	local decldata = require("Module:la-nominal").do_generate_adj_forms(args, pos, "headword", nil, def)
	local lemma = decldata.overriding_lemma
	-- TODO: modify the headword line if the slot isn't the expected one.
	if #lemma == 0 then
		for slot in require("Module:la-nominal").iter_potential_adj_lemma_slots() do
			local potential_lemma = decldata.forms["linked_" .. slot]
			if not form_is_empty(potential_lemma) then
				if decldata.unattested[slot] then
					potential_lemma = "*" .. potential_lemma
				end
				lemma = potential_lemma
				break
			end
		end
	end

	data.heads = lemma
	-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
	-- really any redundant heads.
	data.no_redundant_head_cat = true
	data.id = decldata.id

	local categories = data.categories
	if is_num then
		process_num_type(decldata.num_type, categories)
	end

	if decldata.num == "pl" then
		insert_if_not(categories, decldata.pos .. "พหูพจน์เท่านั้นภาษาละติน")
	end

	if decldata.indecl then
		insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})

		if decldata.pos == "รูปกริยาขยาย" then
			if lemma[1]:match("um$") then
				insert_if_not(categories, "Latin perfect participles")
			end
		end
	else
		local lemma_num = decldata.num == "pl" and "pl" or "sg"
		local masc = decldata.forms["nom_" .. lemma_num .. "_m"]
		local fem = attested_form(decldata, "nom_" .. lemma_num .. "_f")
		local neut = attested_form(decldata, "nom_" .. lemma_num .. "_n")
		local gen = attested_form(decldata, "gen_" .. lemma_num .. "_m")
		local acc = attested_form(decldata, "acc_" .. lemma_num .. "_m")
	
		if decldata.pos == "รูปกริยาขยาย" then
			local masc1 = masc[1]
			if masc1:sub(-5) == "ūrus" then
				insert_if_not(categories, "Latin future active participles")
			elseif masc1:sub(-4) == "ndus" then
				-- FIXME, should rename to "Latin gerundives")
				insert_if_not(categories, "Latin future passive participles")
			else
				local masc1_final2 = masc1:sub(-2)
				if masc1_final2 == "us" then
					insert_if_not(categories, "Latin perfect participles")
				elseif masc1_final2 == "ns" then
					insert_if_not(categories, "Latin present participles")
				else
					error("Unrecognized participle ending: " .. masc1)
				end
			end
		end
	
		-- We display the inflections in three different ways to mimic the
		-- old way of doing things:
		--
		-- 1. If masc and fem are different, show masc, fem and neut.
		-- 2. Otherwise, if masc and neut are different, show masc and neut.
		-- 3. Otherwise, show masc nominative and masc genitive.
		if not form_is_empty(fem) and not deep_equals(masc, fem) then
			fem.label = "เพศหญิง"
			insert(data.inflections, fem)
			if not form_is_empty(neut) then
				neut.label = "เพศกลาง"
				insert(data.inflections, neut)
			end
		elseif not form_is_empty(neut) and not deep_equals(masc, neut) then
			neut.label = "เพศกลาง"
			insert(data.inflections, neut)
		elseif not form_is_empty(gen) then
			gen.label = "สัมพันธการก"
			insert(data.inflections, gen)
		elseif not form_is_empty(acc) then
			acc.label = "กรรมการก"
			insert(data.inflections, acc)
		end

		insert(infl_classes, decldata.title)
	end

	local comp = normalize_derivation(decldata.comp, "ขั้นกว่า")
	local sup = normalize_derivation(decldata.sup, "ขั้นสุด")
	degree_derivations(comp, sup, data)

	local adv = normalize_derivation(decldata.adv, "คำกริยาวิเศษณ์")
	if adv and adv ~= "-" then
		insert(data.inflections, adv)
	end

	for _, cat in ipairs(decldata.categories) do
		insert_if_not(categories, cat)
	end

	for _, cat in ipairs(decldata.cat) do
		insert_if_not(categories, cat .. "ภาษาละติน")
	end
	
	return decldata.pos
end

pos_functions["คำคุณศัพท์"] = function(def, args, data, infl_classes, title)
	return adjectives("คำคุณศัพท์", def, args, data, infl_classes, title)
end

pos_functions["รูปกริยาขยาย"] = function(def, args, data, infl_classes, title)
	return adjectives("รูปกริยาขยาย", def, args, data, infl_classes, title)
end

pos_functions["ตัวกำหนด"] = function(def, args, data, infl_classes, title)
	return adjectives("ตัวกำหนด", def, args, data, infl_classes, title)
end

pos_functions["คำสรรพนาม"] = function(def, args, data, infl_classes, title)
	return adjectives("คำสรรพนาม", def, args, data, infl_classes, title)
end

pos_functions["รูปปัจจัยเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
	return adjectives("ปัจจัย", def, args, data, infl_classes, title)
end

pos_functions["รูปเลขเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
	return adjectives("เลข", def, args, data, infl_classes, title)
end

pos_functions["คำกริยาวิเศษณ์"] = function(def, args, data)
	local sublist = {sublist = "/"}
	args = require("Module:parameters").process(args, {
		[1] = {alias_of = "head", list = false},
		[2] = {alias_of = "comp"},
		[3] = {alias_of = "sup"},
		["head"] = {list = true, required = true},
		["comp"] = sublist,
		["sup"] = sublist,
		["id"] = true,
	})
	data.heads = args.head
	data.no_redundant_head_cat = true -- since head= is required
	data.id = args.id
	local comp, sup = args.comp, args.sup
	local irreg = false

	if comp then
		if comp[1] == "-" then
			comp = "-"
		elseif comp[1] == nil then
			comp = nil
		else
			comp.label = glossary_link("ขั้นกว่า")
			comp = args.comp
			irreg = true
		end
	end
	if sup then
		if sup[1] == "-" then
			sup = "-"
		elseif sup[1] == nil then
			sup = nil
		else
			sup.label = glossary_link("ขั้นสุด")
			sup = args.sup
			irreg = true
		end
	end

	local categories = data.categories
	if irreg then
		insert_if_not(categories, "คำกริยาวิเศษณ์ไม่ปรกติภาษาละติน")
	end

	if not (comp or sup) then
		local default_comp = {label = glossary_link("ขั้นกว่า")}
		local default_sup = {label = glossary_link("ขั้นสุด")}
		for _, head in ipairs(args.head) do
			local stem = nil
			for _, suff in ipairs{"iter", "nter", "ter", "er", "iē", "ē", "im", "ō"} do
				stem = head:match("(.*)" .. pattern_escape(suff) .. "$")
				if stem ~= nil then
					if suff == "nter" then
						stem = stem .. "nt"
						suff = "er"
					end
					insert(default_comp, stem .. "ius")
					insert(default_sup, stem .. "issimē")
					break
				end
			end
			if not stem then
				error("Unrecognized adverb type, recognized types are “-ē”, “-er”, “-ter”, “-iter”, “-im”, or “-ō” or specify irregular forms or “-” if incomparable.")
			end
		end
		comp = comp or default_comp
		sup = sup or default_sup
	end

	degree_derivations(comp, sup, data)
end

pos_functions["รูปปัจจัยเป็นกริยาวิเศษณ์"] = pos_functions["คำกริยาวิเศษณ์"]

local function get_forms(forms)
	if #forms == 0 then
		return nil
	end
	local i, attested = 1, false
	while true do
		local form = forms[i]
		if form == nil then
			return forms, attested
		elseif form == "-" then
			remove(forms, i)
		else
			if not (attested or get_plaintext(form):sub(1, 1) == "*") then
				attested = true
			end
			i = i + 1
		end
	end
end

local function degree(pos, deg, pos_func, other_arg, other_label, args, data, infl_classes)
	local list = {list = true}
	args = require("Module:parameters").process(args, {
		[1] = {alias_of = "head", list = false},
		["head"] = list,
		["positive"] = list,
		[other_arg] = list,
		["id"] = true,
	})
	data.no_redundant_head_cat = #args.head == 0
	-- Set default manually so we can tell whether the user specified head=.
	if #args.head == 0 then
		args.head = {PAGENAME}
	end
	data.heads = args.head
	data.id = args.id

	insert(data.inflections, {label = deg})
	
	if pos_func then
		pos_func(args, data, infl_classes)
	end

	local positive, positive_attested = get_forms(args.positive)

	if positive then
		if not positive_attested then
			insert(data.categories, pos .. deg .. "เท่านั้นภาษาละติน")
		end
		if #positive > 0 then
			args.positive.label = "เชิงบวก"
			insert(data.inflections, args.positive)
		else
			insert(data.inflections, {label = "ไม่มีรูปเชิงบวก"})
		end
	end

	local other = get_forms(args[other_arg])
	if other then
		if #other > 0 then
			args[other_arg].label = other_label
			insert(data.inflections, args[other_arg])
		else
			insert(data.inflections, {label = "ไม่มีรูป" .. other_label})
		end
	end

	-- If a lemma, return the primary part of speech ("adjectives" or
	-- "adverbs"), so that the term is categorized in "Latin adjectives" or
	-- "Latin adverbs". Otherwise, return nothing, so that the term goes in the
	-- relevant non-lemma category (e.g. "Latin comparative adjectives"), and
	-- into "Latin non-lemma forms".
	if positive and not positive_attested then
		return pos
	end
end

local function comp_adj(args, data, infl_classes)
	insert(infl_classes, "[[ภาคผนวก:Latin third declension|third declension]]")
	local n = {label = "neuter"}
	for _, head in ipairs(args.head) do
		local neuter = head:gsub("or$", "us")
		insert(n, neuter)
	end
	insert(data.inflections, n)
end

pos_functions["คำคุณศัพท์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
	return degree("คำคุณศัพท์", "ขั้นกว่า", comp_adj, "sup", "ขั้นสุด", args, data, infl_classes)
end

pos_functions["คำกริยาวิเศษณ์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
	return degree("คำกริยาวิเศษณ์", "ขั้นกว่า", nil, "sup", "ขั้นสุด", args, data, infl_classes)
end

local function sup_adj(args, data, infl_classes)
	insert(infl_classes, "[[ภาคผนวก:Latin first declension|first]]")
	insert(infl_classes, "[[ภาคผนวก:Latin second declension|second declension]]")
	local f, n = {label = "เพศหญิง"}, {label = "เพศกลาง"}
	for _, head in ipairs(args.head) do
		local stem = head:gsub("us$", "")
		insert(f, stem .. "a")
		insert(n, stem .. "um")
	end
	insert(data.inflections, f)
	insert(data.inflections, n)
end

pos_functions["คำคุณศัพท์ขั้นสุด"] = function(def, args, data, infl_classes, title)
	return degree("คำคุณศัพท์", "ขั้นสุด", sup_adj, "comp", "ขั้นกว่า", args, data, infl_classes)
end

pos_functions["คำกริยาวิเศษณ์ขั้นสุด"] = function(def, args, data, infl_classes, title)
	return degree("คำกริยาวิเศษณ์", "ขั้นสุด", nil, "comp", "ขั้นกว่า", args, data, infl_classes)
end

local function adpositions(pos, def, args, data, infl_classes, title, postscript)
	local cases = invert(require("Module:la-utilities").cases)

	args = require("Module:parameters").process(args, {
		[1] = {alias_of = "head", list = false},
		[2] = {list = true, set = keys_to_list(cases)},
		["head"] = {list = true, required = true},
		["id"] = true,
	})

	-- Case names are supplied in numbered arguments, optionally preceded by
	-- headwords.
	cases = args[2]
	for i = 1, #cases do
		for j = i + 1, #cases do
			if cases[i] == cases[j] then
				error("Duplicate case")
			end
		end
		local case = cases[i]
		local appendix_link = glossary_link(case)
		if i == 1 then
			appendix_link = "+ " .. appendix_link
		end
		insert(postscript, appendix_link)
		insert_if_not(data.categories, "Latin " .. case .. " " .. pos)
	end

	data.heads = args.head
	data.no_redundant_head_cat = true -- since head= is required
	data.id = args.id
end

pos_functions["คำบุพบท"] = function(...)
	return adpositions("คำบุพบท", ...)
end

pos_functions["คำปัจฉบท"] = function(...)
	return adpositions("คำปัจฉบท", ...)
end

pos_functions["รูปกริยาเป็นนาม"] = function(def, args, data)
	args = require("Module:parameters").process(args, {
		[1] = {required = true, default = "labōrandum"}, -- headword
		[2] = true, -- gerundive
	})

	data.heads = {args[1]}
	data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
	insert(data.inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#กรรมการก|กรรมการก]]"})
	local stem = args[1]:match("^(.*)um$")
	if not stem then
		error("Unrecognized gerund ending: " .. stem)
	end
	if args[2] == "-" then
		insert(data.inflections, {label = "no [[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
	else
		insert(data.inflections, {[1] = args[2] or stem .. "us", label = "[[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
	end
end

local function non_lemma_forms(def, args, data)
	args = require("Module:parameters").process(args, {
		[1] = {required = true, default = def}, -- headword or cases
		["head"] = {list = true, require_index = true},
		["g"] = {list = true},
		["id"] = true,
	})

	local heads = {args[1]}
	for _, head in ipairs(args.head) do
		insert(heads, head)
	end
	data.heads = heads
	data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
	data.genders = args.g
	data.id = args.id
end

pos_functions["รูปนาม"] = non_lemma_forms
pos_functions["รูปวิสามานยนาม"] = non_lemma_forms
pos_functions["รูปสรรพนาม"] = non_lemma_forms
pos_functions["รูปกริยา"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาเป็นนาม"] = non_lemma_forms
pos_functions["รูปคุณศัพท์"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาขยาย"] = non_lemma_forms
pos_functions["รูปผันตัวกำหนด"] = non_lemma_forms
pos_functions["รูปเลข"] = non_lemma_forms
pos_functions["รูปปัจจัย"] = non_lemma_forms

return export