모듈:Ancient Greek

local p = {}
local macron = mw.ustring.char(0x304)
local breve = mw.ustring.char(0x306)
local rough = mw.ustring.char(0x314)
local smooth = mw.ustring.char(0x313)
local diaeresis = mw.ustring.char(0x308)
local acute = mw.ustring.char(0x301)
local grave = mw.ustring.char(0x300)
local circumflex = mw.ustring.char(0x342)
local Latin_circumflex = mw.ustring.char(0x302)
local subscript = mw.ustring.char(0x345)
local macron_circumflex = macron .. diaeresis .. '?' .. Latin_circumflex
local is_velar = { 'κ' = true, 'γ' = true, 'χ' = true, 'ξ' = true, }
local UTF8_char = "%z\1-\127\194-\244\128-\191*"
local basic_Greek = "\206-\207\128-\191" -- excluding first line of Greek and Coptic block: ͰͱͲͳʹ͵Ͷͷͺͻͼͽ;Ϳ
local info = {}
-- The tables are shared among different characters so that they can be checked
-- for equality if needed, and to use less space.
local vowel = { vowel = true, diacritic_seat = true }
local iota = { vowel = true, diacritic_seat = true, offglide = true }
local upsilon = { vowel = true, diacritic_seat = true, offglide = true }
-- Technically rho is only a seat for rough or smooth breathing.
local rho = { consonant = true, diacritic_seat = true }
local consonant = { consonant = true }
local diacritic = { diacritic = true }
-- Needed for equality comparisons.
local breathing = { diacritic = true }
local function add_info(characters, t)
	if type(characters) == "string" then
		for character in string.gmatch(characters, UTF8_char) do
			infocharacter = t
		end
	else
		for _, character in ipairs(characters) do
			infocharacter = t
		end
	end
end
add_info({ macron, breve,
		diaeresis,
		acute, grave, circumflex,
		subscript,
	}, diacritic)
add_info({rough, smooth}, breathing)
add_info("ΑΕΗΟΩαεηοω", vowel)
add_info("Ιι", iota)
add_info("Υυ", upsilon)
add_info("ΒΓΔΖΘΚΛΜΝΞΠΡΣΤΦΧΨϜϘϺϷͶϠβγδζθκλμνξπρσςτφχψϝϙϻϸͷϡ", consonant)
add_info("Ρρ", rho)
local not_recognized = {}
setmetatable(info, { __index =
	function()
		return not_recognized
	end
})
local function quote(str)
	return "“" ..  str .. "”"
end
local correspondences = {
	-- Vowels
	"α" = "a",
	"ε" = "e",
	"η" = "e" .. macron,
	"ι" = "i",
	"ο" = "o",
	"υ" = "u",
	"ω" = "o" .. macron,
	-- Consonants
	"β" = "b",
	"γ" = "g",
	"δ" = "d",
	"ζ" = "z",
	"θ" = "th",
	"κ" = "k",
	"λ" = "l",
	"μ" = "m",
	"ν" = "n",
	"ξ" = "x",
	"π" = "p",
	"ρ" = "r",
	"σ" = "s",
	"ς" = "s",
	"τ" = "t",
	"φ" = "ph",
	"ψ" = "ps",
	
	-- Archaic letters
	"ϝ" = "w",
	"ϻ" = "ś",
	"ϙ" = "q",
	"ϡ" = "š",
	"ͷ" = "v",
	
	-- Diacritics
	smooth = ,
	rough = , -- h is added below in the `transliterate` function.
	breve = ,
}
local ALA_LC = {
	"χ" = "ch",
	acute = ,
	grave = ,
	circumflex = ,
	subscript = ,
	diaeresis = ,
	macron = ,
}
local Wiktionary_transliteration = {
	"χ" = "kh",
	circumflex = Latin_circumflex,
	subscript = 'i',
}
local function add_index_metamethod(t, index_metamethod)
	local mt = getmetatable(t)
	if not mt then
		mt = {}
		setmetatable(t, mt)
	end
	mt.__index = index_metamethod
end
--=
		This breaks a word into meaningful "tokens", which are
		individual letters or diphthongs with their diacritics.
		Used by Module:grc-accent and Module:grc-pronunciation.
--=
local function tokenize(text)
	local tokens, vowel_info, prev_info = {}, {}, {}
	local token_i = 1
	local prev
	for character in string.gmatch(mw.ustring.toNFD(text), UTF8_char) do
		local curr_info = infocharacter
		-- Split vowels between tokens if not a diphthong.
		if curr_info.vowel then
			if prev and (not (curr_info.offglide and prev_info.vowel)
					-- υυ → υ, υ
					-- ιυ → ι, υ
					or prev_info.offglide and curr_info == upsilon) then
				token_i = token_i + 1
			end
			tokenstoken_i = (tokenstoken_i or "") .. character
			table.insert(vowel_info, { index = token_i })
		elseif curr_info.diacritic then
			tokenstoken_i = (tokenstoken_i or "") .. character
			if prev_info.vowel or prev_info.diacritic then
				if character == diaeresis then
					-- Current token is vowel, vowel, possibly other diacritics,
					-- and a diaeresis.
					-- Split the current token into two:
					-- the first letter, then the second letter plus any diacritics.
					local previous_vowel, vowel_with_diaeresis = string.match(tokenstoken_i, "^(" .. basic_Greek .. ")(" .. basic_Greek .. ".+)")
					if previous_vowel then
						tokenstoken_i, tokenstoken_i + 1 = previous_vowel, vowel_with_diaeresis
						token_i = token_i + 1
					end
				end
			elseif prev_info == rho then
				if curr_info ~= breathing then
					return string.format("The character %s cannot have the accent %s on it.", prev, "◌" .. character)
				end
			else
				error("The character " .. quote(prev) .. " cannot have a diacritic on it.")
			end
		elseif curr_info == rho then
			if prev and not (prev_info == breathing and infostring.match(tokenstoken_i, "^" .. basic_Greek) == rho) then
				token_i = token_i + 1
			end
			tokenstoken_i = (tokenstoken_i or "") .. character
		else
			if prev then
				token_i = token_i + 1
			end
			tokenstoken_i = (tokenstoken_i or "") .. character
		end
		prev = character
		prev_info = curr_info
	end
	return tokens
end
function p.transliterate(text, system)
	add_index_metamethod(correspondences, system == "ALA-LC" and ALA_LC or Wiktionary_transliteration)
	
	if text == '῾' then
		return 'h'
	end
	
	text = mw.ustring.toNFD(text)
	
	--
		Replace semicolon or Greek question mark with regular question mark,
		except after an ASCII alphanumeric character (to avoid converting
		semicolons in HTML entities).
	--
	text = mw.ustring.gsub(text, "(^A-Za-z0-9);" .. mw.ustring.char(0x37E) .. "", "%1?")
	
	-- Handle the middle dot. It is equivalent to semicolon or colon, but semicolon is probably more common.
	text = text:gsub("·", ";")
	
	local tokens = tokenize(text)
	--now read the tokens
	local output = {}
	for i, token in pairs(tokens) do
		-- substitute each character in the token for its transliteration
		local translit = string.gsub(mw.ustring.lower(token), UTF8_char, correspondences)
		
		if token == 'γ' and is_velartokensi + 1 then
			-- γ before a velar should be 
			translit = 'n'
		elseif token == 'ρ' and tokensi - 1 == 'ρ' then
			-- ρ after ρ should be 
			translit = 'rh'
		elseif system == "Wiktionary" and mw.ustring.find(token, '^αΑ.*' .. subscript .. '$') then
			-- add macron to ᾳ
			translit = mw.ustring.gsub(translit, '(aA)', '%1' .. macron)
		end
		
		if token:find(rough) then
			if mw.ustring.find(token, 'Ρρ') then
				translit = translit .. 'h'
			else -- vowel
				translit = 'h' .. translit
			end
		end
		
		if system == "ALA-LC" and mw.ustring.find(token, '^υΥ^ιΙ*$') then
			translit = translit:gsub('u', 'y'):gsub('U', 'Y')
		end
		
		-- Remove macron from a vowel that has a circumflex.
		if mw.ustring.find(translit, macron_circumflex) then
			translit = translit:gsub(macron, )
		end
		
		-- Capitalize first character of transliteration.
		if token ~= mw.ustring.lower(token) then
			translit = mw.ustring.gsub(translit, "^.", mw.ustring.upper)
		end
		
		table.insert(output, translit)
	end
	
	return table.concat(output)
end
function p.translit(frame)
	local text = frame.args1 or frame:getParent().args1
	
	local system = frame.args.system
	if system == nil or system == "" then
		system = "Wiktionary"
	elseif not (system == "ALA-LC" or system == "Wiktionary") then
		error('Transliteration system in |system= not recognized; choose between "ALA-LC" and "Wiktionary"')
	end
	
	local transliteration = p.transliterate(text, system)
	return  .. transliteration .. 
end
function p.bare_translit(frame)
	return p.transliterate(frame.args1 or frame:getParent().args1)
end
return p