Module:cy-mut

From Wiktionary, the free dictionary
Jump to navigation Jump to search

This module powers Welsh mutation tables, including automatically generating the correct mutations for a given word.

Parameters

|1=
The radical form of the word. This parameter is unnamed, so can be specified by position alone.
|soft=
Explicitly specify the soft mutation. If this is different to what would be expected, it will be marked as irregular.
|nasal=
Explicitly specify the nasal mutation. If this is different to what would be expected, it will be marked as irregular.
|aspirate=
Explicitly specify the aspirate mutation. If this is different to what would be expected, it will be marked as irregular.
|nocat=
If set to a true value, e.g |nocat=1, then the resulting table will not contain any page categories. If left default or set to a false value then categories may get set, for example Welsh terms with irregular mutation in the case of mutation tables with at least one irregular value.

local export = {}

local yesno = require("Module:yesno")

local toNFC = mw.ustring.toNFC
local toNFD = mw.ustring.toNFD
local ulower = mw.ustring.lower
local umatch = mw.ustring.match
local usub = mw.ustring.sub

local GRAVE = "\204\128"
local ACUTE = "\204\129"
local CIRC = "\204\130"
local DIAER = "\204\136"

local lang = require("Module:languages").getByCode("cy")
local PAGENAME = mw.loadData("Module:headword/data").pagename
local MARKER = "<sup>△</sup>"

local mutation_rules = {
	["b"] = {"f", "m"},
	["c"] = {"g", "ngh", "ch"},
	["ch"] = true,
	["d"] = {"dd", "n"},
	["dd"] = true,
	["f"] = true,
	["g"] = {"", "ng"},
	["h"] = true,
	["j"] = true,
	["k"] = true,
	["l"] = true,
	["ll"] = {"l"},
	["m"] = {"f"},
	["mh"] = true,
	["n"] = true,
	["p"] = {"b", "mh", "ph"},
	["ph"] = true,
	["q"] = true,
	["r"] = true,
	["rh"] = {"r"},
	["s"] = true,
	["t"] = {"d", "nh", "th"},
	["th"] = true,
	["ts"] = true,
	["tsi"] = {"j", colloquial = true}, -- TODO: add colloquial marker.
	["v"] = true,
	["x"] = true,
	["z"] = true
}

function export.get_mutation_data(term)
	local data = {radical = term}
	
	local term_lower = term:gsub("^.[\128-\191]*", ulower) -- term with lowercase initial
	data.is_uppercase = term_lower ~= term
	local normalized = toNFD(term_lower)
	data.vowel = normalized:match("^[aeiouwy]") and true or false
	
	if data.vowel then
		data.final = term_lower
		-- H-prothesis, unless initial "w" is a semivowel (followed by a vowel other than "y", or "y" with a diacritic).
		if not (normalized:match("^w[aeiouw]") or umatch(normalized, "^wy[" .. GRAVE .. ACUTE .. CIRC .. DIAER .. "]")) then
			data.mut3 = "h"
		end
		return data
	end
	
	local initial, final, mut
	for i = 3, 1, -1 do
		initial = usub(normalized, 1, i)
		mut = mutation_rules[initial]
		if mut then
			final = usub(normalized, i + 1)
			break
		elseif i == 1 then
			error(("no mutation rule found for %s"):format(term))
		end
	end
	
	if type(mut) == "table" then
		for k, v in pairs(mut) do
			if tonumber(k) then
				data["mut" .. k] = v
			end
		end
		data.colloquial = mut.colloquial
	end
	
	if initial == "tsi" then
		-- "i" in "tsi" is retained if:
		-- followed by a consonant (e.g. "tsips" → "jips")
		-- followed by a semivowel (e.g. "tsiwawa" → "jiwawa", but "*tsiŵawa" → "*jŵawa"); "i" and "w" are semivowels if followed by any vowel, due to the preceding "i".
		-- it has a diacritic (e.g. "tsïars" → "jïars")
		if not (final:match("^[aeiouwy]") and not final:match("^[iw][aeiouwy]")) then
			final = "i" .. final
		-- If "i" in "tsi" is not retained, the following vowel cannot have a diaeresis unless it's followed by a vowel.
		elseif not umatch(final, "^[aeiouwy]" .. DIAER .. "[" .. GRAVE .. ACUTE .. CIRC .. "]?[aeiouwy]") then
			final = final:gsub("^([aeiouwy])" .. DIAER, "%1") -- If there's no diaeresis, this does nothing.
		end
	end
	
	data.final = toNFC(final)
	
	return data
end

local function construct_mutation(data, accel_form, initial, override)
	local normal_mutation
	if not initial then
		normal_mutation = nil
	else
		normal_mutation = initial .. data.final
		if data.is_uppercase then
			normal_mutation = require("Module:string utilities").ucfirst(normal_mutation)
		end
	end
	local has_override = override
	if not yesno(override, true) or override == "-" then -- TODO: yesno to be removed.
		override = nil
	end
	local has_irreg_mutation = has_override and override ~= normal_mutation
	local mutation
	-- don't combine the following into A and B or C because `override` may be nil
	if has_override then
		mutation = override
	else
		mutation = normal_mutation
	end
	local marker = has_irreg_mutation and MARKER or ""
	if mutation then
		return require("Module:links").full_link({lang = lang, accel = {form = accel_form, lemma = data.radical}, term = mutation}) .. marker, true, has_irreg_mutation
	else
		return "''unchanged''" .. marker, false, has_irreg_mutation
	end
end

function export.show(frame)
	local params = {
		[1] = {},
		["soft"] = {},
		["nasal"] = {},
		["aspirate"] = {},
		["nocat"] = {type = "boolean"},
	}
	local parargs = frame:getParent().args
    local args = require("Module:parameters").process(parargs, params)
	
	local title = args[1] or PAGENAME
	local data = export.get_mutation_data(title)

	local soft, has_soft, has_irreg_soft = construct_mutation(data, "soft", data.mut1, args.soft)
	local nasal, has_nasal, has_irreg_nasal  = construct_mutation(data, "nasal", data.mut2, args.nasal)
	local aspirate, has_aspirate, has_irreg_aspirate = construct_mutation(data, data.vowel and "h-prothesis" or "aspirate", data.mut3, args.aspirate)

	local result = frame:expandTemplate{ title = 'inflection-table-top', 
		args = { title = '[[Appendix:Welsh mutations|Mutated forms]] of ' .. require("Module:links").full_link({lang = lang, alt = title}, 'term'), palette = 'green' } }
	result = result .. '\n! [[radical]]'
	result = result .. '\n! [[soft mutation|soft]]'
	result = result .. '\n! [[nasal mutation|nasal]]'
	result = result .. '\n! ' .. (data.vowel and '[[h-prothesis]]' or '[[aspirate mutation|aspirate]]')
	result = result .. '\n|-'
	result = result .. '\n| ' .. require("Module:links").full_link({lang = lang, term = data.radical})
	result = result .. '\n| ' .. soft
	result = result .. '\n| ' .. nasal
	result = result .. '\n| ' .. aspirate
	notes = ''
	if has_irreg_soft or has_irreg_nasal or has_irreg_aspirate then
		notes = notes .. '<p style="font-size:85%;">' .. MARKER .. 'Irregular.</p>'
	end
	if has_soft or has_nasal or has_aspirate then
		notes = notes .. '<p style="font-size:85%;"><i>Note:</i> Certain mutated forms of some words can never occur in standard Welsh.<br>All possible mutated forms are displayed for convenience.</p>'
	end
	result = result .. '\n' .. frame:expandTemplate{ title = 'inflection-table-bottom', args = { notes = notes } }
	if not args.nocat and (has_irreg_soft or has_irreg_nasal or has_irreg_aspirate) then
		result = result .. require("Module:utilities").format_categories({"Welsh terms with irregular mutation"}, lang)
	end
	return result
end

return export