This module implements the {{haw-IPA}} template.


local export = {}

local m_str_utils = require("Module:string utilities")

local decomp = mw.ustring.toNFD
local find = m_str_utils.find
local gmatch = m_str_utils.gmatch
local gsub = m_str_utils.gsub
local len = m_str_utils.len
local lower = m_str_utils.lower
local sub = m_str_utils.sub

local lang = require("Module:languages").getByCode("haw")
local ipa = require("Module:IPA")

local U = require("Module:string/char")
local breve = U(0x032F)
local macron = U(0x0304)

local cons = "bcdfghklmnpqrstvwzʻ"
local vowels = "aeiouāēīōū"
local diphs = {
	"āi", "āe", "āo", "āu", "ēi", "ōu",
	"ae", "ai", "ao", "au", "ei", "eu", "iu", "ou", "oi"
}
local respell = {
	["cdgqstz"] = "k",
	["bf"] = "p",
	["r"] = "l",
	["v"] = "w"
}

local stressed = "ˈˌ"
local boundary = "%.ˈˌ%+-"

-- syllabify word
local function syllabify(word)
	-- mark diphthongs
	for _, diph in ipairs(diphs) do
		word = gsub(word, diph, "{" .. diph .. "}")
	end

	-- split between vowel and consonant
	word = gsub(word, "([" .. vowels .. "]}?)([" .. cons .. "])", "%1.%2")

	-- split between vowels
	word = gsub(word, "([^{][" .. vowels .. "]}?)({?[" .. vowels .. "][^}])", "%1.%2")

	-- remove diphthong placeholder
	word = gsub(word, "[{}]", "")

	-- syllables that don't end in vowels are invalid
	if find(word, cons .. "[^" .. vowels .."]") then
		error("One of the entries doesn't match Hawaiian syllable structure.")
	end

	return word
end

-- determine word stress
local function stress(word)
	-- convert macron vowels to double vowels
	word = gsub(decomp(word), "([" .. vowels .. "])" .. macron, "%1%1")

	-- prepare for stress assignment
	local last_vowel_pos = len(gsub(word, "[^" .. vowels .. "]", "")) - 1

	-- assign stress mark to the second last vowel
	return gsub(gsub(word, "([" .. vowels .. "])", function(v)
		last_vowel_pos = last_vowel_pos - 1
		-- shift it after space or syllable boundary
		return (last_vowel_pos == 0) and 'ˈ' .. v or v
	end), "([^" .. boundary .. "]*)ˈ([^.]*)", "ˈ%1%2")
end

-- generate pronunciation of words
function export.toIPA(entry, is_phonetic)
	if type(entry) == "table" then
		entry = table.concat(entry)
	end

	-- make entry lowercase
	entry = lower(entry)

	-- validate characters
	if not find(entry, "[aābcdeēfghiīklmnoōpqrstuūvwzʻ %+-]") then
		error("Invalid characters. Only valid Hawaiian letters, spaces and symbols '+' and '-' are accepted.")
	end

	-- syllabify entry
	entry = syllabify(entry)

	-- syllabify and stress entry
	local result = ""
	for segment in gmatch(syllabify(entry), "[^ %+-]+[ %+-]?") do
		-- apply stress to each segment
		segment = stress(segment)

		-- convert to secondary stress if part of compound
		if find(segment, "[%+-]") then
			segment = gsub(segment, "ˈ", "ˌ"):gsub("[%+-]", ".")
		end

		result = result .. segment
	end

	-- remove syllable boundaries before stress
	result = gsub(result, "%.([" .. stressed .. "])", "%1")

	-- mark long vowels
	result = gsub(result, "([" .. vowels .. "])%1", "%1ː")

	-- mark diphthongisation
	result = gsub(result, "([^j])iu", "%1ju"):gsub("([^w])oi", "%1o" .. breve .. "i")
	for _, vowel in ipairs(diphs) do
		result = gsub(result, vowel, vowel .. breve)
	end

	-- replace respellt consonants
	for i, conso in pairs(respell) do
		for j = 1, len(i) do
			local c = sub(i, j, j)
			result = gsub(result, c, conso)
		end
	end

	-- real value of glottal stop
	result = gsub(result, "ʻ", "ʔ")

	-- phonetic transcriptions
	if is_phonetic then
		-- allophony of /k/ before /i/
		result = gsub(result, "ki", "ti")

		-- real pronunciation of /w/
		result = gsub(result, "w", "ʋ")

		-- allophony of glides /j/, /w/
		result = gsub(result, "([ei]" .. breve .. "?[" .. boundary .. "])([aou])", "%1j%2")
		result = gsub(result, "([ou]" .. breve .. "?[" .. boundary .. "])([aei])", "%1w%2")

		-- actual pronunciation of diphthongs
		result = gsub(result, "i" .. breve, "j")
		result = gsub(result, "u" .. breve, "w")

		-- allophony of /a/
		result = gsub(result, "([" .. stressed .. "][^" .. vowels .. "]?)a", "%1ɐ")
		result = gsub(result, "a", "ə")
		result = gsub(result, "[ɐə]ː", "aː")

		-- allophony of /e/
		result = gsub(result, "([" .. stressed .. "][^" .. vowels .. "]?)e", "%1ɛ")
		result = gsub(result, "e([" .. boundary .. "][^" .. vowels .. "]*)ɛ", "ɛ%1ɛ")
		result = gsub(result, "ɛ([" .. boundary .. "][^" .. vowels .. "]*)e", "ɛ%1ɛ")
		result = gsub(result, "ɛː", "eː")
	end

	return result
end

-- rapid pronunciation of diphthongs
function export.rapid(entry)
	return gsub(entry, "ɐj", "ɛj"):gsub("ɐw", "ɔw")
end

-- display pronunciation
function export.show(frame)
	local args = frame:getParent().args
	local pagetitle = mw.title.getCurrentTitle().text

	local p, results = {}, {}

	if args[1] then
		for _, v in ipairs(args) do
			table.insert(p, (v ~= "") and v or nil)
		end
	else
		p = { pagetitle }
	end

	for _, word in ipairs(p) do
		local phonemic = export.toIPA(word, false)
		local phonetic = export.toIPA(word, true)
		local rapid = export.rapid(phonetic)

		table.insert(results, { pron = "/" .. phonemic .. "/" })
		if phonemic ~= phonetic then table.insert(results, { pron = "[" .. phonetic .. "]" }) end
		if phonetic ~= rapid then table.insert(results, { pron = "[" .. rapid .. "]", qq = {"rapid speech"} }) end
	end

	return ipa.format_IPA_full(lang, results)
end

return export