Module:rad-IPA: Difference between revisions
Jump to navigation
Jump to search
TheNightAvl (talk | contribs) (Added stress prediction) |
TheNightAvl (talk | contribs) (Added phonetic option) |
||
Line 130: | Line 130: | ||
} | } | ||
local valid_phone = { | local valid_phone = { | ||
["a"] = true, ["aː"] = true, ["aːː"] = true, ["æː"] = true, ["ai"] = true, | ["a"] = true, ["aː"] = true, ["aːː"] = true, ["æː"] = true, ["ai"] = true, | ||
Line 144: | Line 143: | ||
["uːː"] = true, ["uːo"] = true, ["ua"] = true, ["uo"] = true, ["v"] = true, | ["uːː"] = true, ["uːo"] = true, ["ua"] = true, ["uo"] = true, ["v"] = true, | ||
["w"] = true, ["y"] = true, ["z"] = true, ["ʒ"] = true, ["θ"] = true, | ["w"] = true, ["y"] = true, ["z"] = true, ["ʒ"] = true, ["θ"] = true, | ||
-- allophones -- | |||
["x"] = true, ["ʝ"] = true, | |||
} | } | ||
local valid_phone_temp = {"iːj", "uːj", "yːj", "ù", "ū", "ĵ"} | local valid_phone_temp = {"iːj", "uːj", "yːj", "ù", "ū", "ĵ"} | ||
Line 159: | Line 162: | ||
["t"] = true, ["ts"] = true, ["tʃ"] = true, ["v"] = true, ["w"] = true, | ["t"] = true, ["ts"] = true, ["tʃ"] = true, ["v"] = true, ["w"] = true, | ||
["z"] = true, ["ʒ"] = true, ["θ"] = true, | ["z"] = true, ["ʒ"] = true, ["θ"] = true, | ||
-- allophones -- | |||
["x"] = true, ["ʝ"] = true, | |||
} | } | ||
local consonant_temp = {"ĵ"} | local consonant_temp = {"ĵ"} | ||
Line 171: | Line 178: | ||
["t"] = true, ["ts"] = true, ["tʃ"] = true, ["v"] = true, | ["t"] = true, ["ts"] = true, ["tʃ"] = true, ["v"] = true, | ||
["z"] = true, ["ʒ"] = true, ["θ"] = true, | ["z"] = true, ["ʒ"] = true, ["θ"] = true, | ||
-- allophones -- | |||
["x"] = true, | |||
} | } | ||
Line 187: | Line 198: | ||
["t"] = "t", ["ts"] = "ts", ["tʃ"] = "tʃ", ["v"] = "f", | ["t"] = "t", ["ts"] = "ts", ["tʃ"] = "tʃ", ["v"] = "f", | ||
["z"] = "s", ["ʒ"] = "ʃ", ["θ"] = "θ", | ["z"] = "s", ["ʒ"] = "ʃ", ["θ"] = "θ", | ||
-- allophones -- | |||
["x"] = "x", | |||
} | } | ||
Line 195: | Line 210: | ||
["t"] = "d", ["ts"] = "dz", ["tʃ"] = "dʒ", ["v"] = "v", | ["t"] = "d", ["ts"] = "dz", ["tʃ"] = "dʒ", ["v"] = "v", | ||
["z"] = "z", ["ʒ"] = "ʒ", ["θ"] = "ð", | ["z"] = "z", ["ʒ"] = "ʒ", ["θ"] = "ð", | ||
-- allophones -- | |||
["x"] = "ɣ", | |||
} | } | ||
Line 272: | Line 291: | ||
["t"] = true, ["d"] = true, | ["t"] = true, ["d"] = true, | ||
-- allophones -- | |||
["x"] = true, | |||
} | } | ||
Line 283: | Line 306: | ||
["s"] = true, ["ʃ"] = true, ["z"] = true, ["ʒ"] = true, | ["s"] = true, ["ʃ"] = true, ["z"] = true, ["ʒ"] = true, | ||
-- allophones -- | |||
["x"] = true, | |||
} | } | ||
Line 291: | Line 318: | ||
["t"] = true, ["ts"] = true, ["tʃ"] = true, | ["t"] = true, ["ts"] = true, ["tʃ"] = true, | ||
["z"] = true, ["ʒ"] = true, ["θ"] = true, | ["z"] = true, ["ʒ"] = true, ["θ"] = true, | ||
-- allophones -- | |||
["x"] = true, ["ʝ"] = true, | |||
} | } | ||
Line 299: | Line 330: | ||
["v"] = true, | ["v"] = true, | ||
["z"] = true, ["ʒ"] = true, ["θ"] = true, | ["z"] = true, ["ʒ"] = true, ["θ"] = true, | ||
-- allophones -- | |||
["x"] = true, | |||
} | } | ||
local CN = continuant | local CN = continuant | ||
Line 595: | Line 630: | ||
end | end | ||
function resolve_consonants(phones, hj_dv) | function resolve_consonants(phones, hj_dv, phon) | ||
local working_phones = phones | local working_phones = phones | ||
mw.log("————— BEGINNING CONSONANT RESOLUTION —————") | mw.log("————— BEGINNING CONSONANT RESOLUTION —————") | ||
Line 644: | Line 679: | ||
-- Resolution of hjádvanþs -- | -- Resolution of hjádvanþs -- | ||
if hj_dv | if not hj_dv then | ||
if p_current == "ç" then | if p_current == "ç" then | ||
mw.log(" | mw.log("hj = false:") | ||
p_Convert("ʃ") | p_Convert("ʃ") | ||
elseif p_current == "j" and (boundary[p_prev] or p_prev == nil) and consonant[p_next] then | elseif p_current == "j" and (boundary[p_prev] or p_prev == nil) and consonant[p_next] then | ||
mw.log(" | mw.log("hj = false:") | ||
p_Convert("ʒ") | p_Convert("ʒ") | ||
end | end | ||
Line 686: | Line 721: | ||
mw.log("[d][ʃ] → [dʒ] in position " .. i .. ".") | mw.log("[d][ʃ] → [dʒ] in position " .. i .. ".") | ||
end | end | ||
end | end | ||
Line 729: | Line 765: | ||
mw.log("No cluster found.") | mw.log("No cluster found.") | ||
end | end | ||
end | |||
-- Progressive voicing assimilation of Cv -- | |||
if p_current == "v" and not obstruent_voiced[p_prev] then | |||
p_Resolve("f") | |||
mw.log("[" .. p_current .. "][v] → [" .. p_current .. "][f] in position " .. i .. ".") | |||
end | end | ||
Line 736: | Line 779: | ||
p_RemoveNext() | p_RemoveNext() | ||
mw.log("Geminate [" .. p_current .. "] removed in final position at position " .. i .. ".") | mw.log("Geminate [" .. p_current .. "] removed in final position at position " .. i .. ".") | ||
end | |||
-- PHONETIC RESOLUTION -- | |||
if phon == true then | |||
if p_current == "h" and not vowel[p_next] then | |||
mw.log("phon = true:") | |||
p_Resolve("x") | |||
elseif p_current == "h" and p_next == "j" then | |||
mw.log("phon = true:") | |||
p_Resolve("ç") | |||
p_RemoveNext() | |||
end | |||
end | end | ||
Line 868: | Line 923: | ||
elseif Cr[p_current] and p_next == "r" then -- C | *C*r … | elseif Cr[p_current] and p_next == "r" then -- C | *C*r … | ||
JV(true) -- C | *C*r(J)V | JV(true) -- C | *C*r(J)V | ||
elseif Cv[p_current] and p_next == "v" then -- C | *C*v … | elseif Cv[p_current] and (p_next == "v" or p_next == "f") then -- C | *C*v … | ||
JV(true) -- C | *C*v(J)V | JV(true) -- C | *C*v(J)V | ||
elseif CN[p_current] and nasal[p_next] then -- C | *C*N … | elseif CN[p_current] and nasal[p_next] then -- C | *C*N … | ||
Line 1,145: | Line 1,200: | ||
end | end | ||
end | end | ||
return output | return output | ||
Line 1,153: | Line 1,206: | ||
function resolve_syllables(syllables) | function resolve_syllables(syllables) | ||
mw.log("————— RESOLVING SYLLABLES —————") | |||
if #syllables > 1 then | |||
for i = 1, #syllables - 1 do | |||
for i = 1, # | |||
local onset = "" | local working_cluster = {} | ||
if (syllables[i]["coda"] or syllables[i+1]["onset"]) and syllables[i+1] and type(syllables[i]) == "table" and type(syllables[i+1]) == "table" then | |||
-- mw.log("Resolving clusters in syllables " .. i .. " and " .. i + 1 .. ".") | |||
-- get intersyllabic clusters -- | |||
if syllables[i]["coda"] then | |||
for j = 1, #syllables[i]["coda"] do | |||
table.insert(working_cluster, syllables[i]["coda"][j]) | |||
end | |||
end | |||
if syllables[i+1]["onset"] then | |||
for j = 1, #syllables[i+1]["onset"] do | |||
table.insert(working_cluster, syllables[i+1]["onset"][j]) | |||
end | |||
end | |||
-- mw.log("Cluster to be resolved: " .. table.concat(working_cluster)) | |||
local p_last = working_cluster[#working_cluster] | |||
local p_last2 = working_cluster[#working_cluster-1] | |||
local p_last3 = working_cluster[#working_cluster-2] | |||
local p_last4 = working_cluster[#working_cluster-3] | |||
local new_coda = {} | |||
local new_onset = {} | |||
-- splits the current cluster into new coda and new onset and re-assigns them to the syllables -- | |||
if | local function split(point) | ||
if point < 1 or point > 3 or point == nil then | |||
error("split() must take integer values between 1 to 4 inclusive") | |||
end | |||
for j = #working_cluster + 1 - point, #working_cluster do | |||
table.insert(new_onset, working_cluster[j]) | |||
end | |||
if #working_cluster - point > 0 then | |||
for j = 1, #working_cluster - point do | |||
table.insert(new_coda, working_cluster[j]) | |||
end | |||
end | |||
mw.log("σ" .. i .. " | σ" .. i+1 .. " : … " .. table.concat(new_coda) .. " | " .. table.concat(new_onset) .. " …") | |||
syllables[i]["coda"] = new_coda | |||
syllables[i+1]["onset"] = new_onset | |||
end | end | ||
-- determine syllable boundary -- | |||
if | if glide[p_last] and nasal[p_last2] and CN[p_last3] and p_last4 then -- C|CNJ | ||
split(3) | |||
elseif nasal[p_last] and CN[p_last2] and p_last3 then -- C|CN | |||
split(2) | |||
elseif glide[p_last] and p_last2 == "r" and Cr[p_last3] and p_last4 then -- C|CrJ | |||
split(3) | |||
elseif p_last == "r" and Cr[p_last2] and p_last3 then -- C|Cr | |||
split(2) | |||
elseif glide[p_last] and p_last2 == "l" and Cl[p_last3] and p_last4 then -- C|ClJ | |||
split(3) | |||
elseif p_last == "l" and Cl[p_last2] and p_last3 then -- C|Cl | |||
split(2) | |||
elseif glide[p_last] and (p_last2 == "v" or p_last2 == "f") and Cv[p_last3] and p_last4 then -- C|CvJ | |||
split(3) | |||
elseif (p_last == "v" or p_last == "f") and Cv[p_last2] and p_last3 then -- C|Cv | |||
split(2) | |||
elseif glide[p_last] and p_last2 then -- C|CJ | |||
split(2) | |||
else -- C|C | |||
split(1) | |||
end | end | ||
end | end | ||
end | |||
end | |||
return syllables | |||
end | |||
function syllables_to_string(syllables) | |||
local output = "" | |||
for i = 1, #syllables do | |||
local onset = "" | |||
local coda = "" | |||
local stress="" | |||
if type(syllables[i]) == "table" then | |||
if syllables[i]["onset"] then | |||
onset = table.concat(syllables[i]["onset"]) | |||
end | |||
if syllables[i]["coda"] then | |||
coda = table.concat(syllables[i]["coda"]) | |||
end | |||
if syllables[i]["stress"] then | |||
stress = "ˈ" | |||
end | |||
if syllables[i]["stress2"] then | |||
stress = "ˌ" | |||
end | |||
output = output .. stress .. onset .. syllables[i]["nucleus"] .. coda | |||
else | |||
output = output .. syllables[i] | |||
end | end | ||
end | |||
return output | |||
mw.log( | end | ||
return | |||
function export.generate(frame) | |||
local args = getArgs(frame) | |||
local outputIPA = generate_IPA(args[1]) | |||
local parameters = {} | |||
local p = 2 | |||
mw.log("——— Parameters ———") | |||
while args[p] do | |||
parameters[args[p]] = true | |||
mw.log(args[p] .. " = true") | |||
p = p + 1 | |||
end | |||
outputIPA = resolve_vowels(outputIPA) | |||
outputIPA = resolve_consonants(outputIPA, parameters["hj"], parameters["phon"]) | |||
outputIPA = get_syllables(outputIPA) | |||
outputIPA = tag_syllables(outputIPA) | |||
outputIPA = get_stress(outputIPA) | |||
outputIPA = resolve_syllables(outputIPA) | |||
outputIPA = syllables_to_string(outputIPA) | |||
mw.log(outputIPA) | |||
return outputIPA | |||
end | end |
Revision as of 18:19, 20 August 2023
See {{rad-IPA}}
.
local export = {}
local getArgs = require('Module:Arguments').getArgs
-- DATA --
local data = {
["a"] = {
["i"] = "ai",
["o"] = {
["i"] = "ɔi",
[false] = "ɔː",
},
["u"] = "au",
[false] = "a",
},
["á"] = "aː",
["ả"] = "aːː",
["â"] = "ɤ",
["âi"] = "ɤi",
["b"] = "b",
["c"] = "ts",
["d"] = {
["x"] = "dʒ",
["z"] = "dz",
[false] = "d",
},
["ð"] = "ð",
["e"] = {
["a"] = "æː",
["i"] = "ei",
[false] = "ɛ",
},
["é"] = "eː",
["f"] = "f",
["g"] = "ɡ",
["h"] = "h",
["ħ"] = "ɣ",
["i"] = {
["e"] = {
["a"] = "ia",
["j"] = "iej",
[false] = "ie",
},
[false] = "i",
},
["í"] = "iː",
["ỉ"] = {
["e"] = "iːe",
[false] = "iːː",
},
["j"] = "j",
["ĵ"] = "ĵ",
["k"] = "k",
["ķ"] = "tʃ",
["l"] = "l",
["m"] = "m",
["n"] = "n",
["ņ"] = "ŋ",
["o"] = {
["a"] = "ɔa",
[false] = "ɔ",
},
["ó"] = "oː",
["ø"] = {
["a"] = "œa",
["i"] = "ei",
[false] = "œ",
},
["p"] = "p",
["q"] = "k",
["r"] = "r",
["s"] = "s",
["ș"] = "ʃ",
["t"] = "t",
["u"] = {
["i"] = "ɤi",
["o"] = {
["a"] = "ua",
["j"] = "uoj",
[false] = "uo",
},
[false] = "u",
},
["ù"] = "ù",
["û"] = "ɤ",
["ú"] = "uː",
["ủ"] = {
["o"] = "uːo",
[false] = "uːː",
},
["ū"] = "ū",
["v"] = "v",
["w"] = "w",
["x"] = "ʒ",
["y"] = {
["e"] = {
["a"] = "ia",
["j"] = "yej",
[false] = "ie",
},
[false] = "i",
},
["ỳ"] = "y",
["z"] = "z",
["þ"] = "θ",
["·"] = "·",
["-"] = "-",
[" "] = " ",
["."] = "|",
}
data["à"] = data["a"]
data["è"] = data["e"]
data["ì"] = data["i"]
data["ò"] = data["o"]
data["ý"] = data["í"]
data["ỷ"] = data["ỉ"]
data[","] = data["."]
data[":"] = data["."]
data[";"] = data["."]
data["!"] = data["."]
data["?"] = data["."]
local irregular = {
["eurú"] = "ørú",
["eurov"] = "ørov",
["heņre"] = "heņgre",
["nrao"] = "drao",
["ryņl"] = "ryņgl",
}
local valid_phone = {
["a"] = true, ["aː"] = true, ["aːː"] = true, ["æː"] = true, ["ai"] = true,
["au"] = true, ["b"] = true, ["ç"] = true, ["d"] = true, ["ð"] = true,
["dz"] = true, ["dʒ"] = true, ["eː"] = true, ["ei"] = true, ["ɛ"] = true,
["ɤ"] = true, ["ɤi"] = true, ["f"] = true, ["ɡ"] = true, ["ɣ"] = true,
["h"] = true, ["i"] = true, ["iː"] = true, ["iːː"] = true, ["iːe"] = true,
["ia"] = true, ["ie"] = true, ["j"] = true, ["k"] = true, ["l"] = true,
["m"] = true, ["n"] = true, ["ŋ"] = true, ["oː"] = true, ["œ"] = true,
["œa"] = true, ["øi"] = true, ["ɔ"] = true, ["ɔː"] = true, ["ɔa"] = true,
["ɔi"] = true, ["p"] = true, ["r"] = true, ["s"] = true, ["ʃ"] = true,
["t"] = true, ["ts"] = true, ["tʃ"] = true, ["u"] = true, ["uː"] = true,
["uːː"] = true, ["uːo"] = true, ["ua"] = true, ["uo"] = true, ["v"] = true,
["w"] = true, ["y"] = true, ["z"] = true, ["ʒ"] = true, ["θ"] = true,
-- allophones --
["x"] = true, ["ʝ"] = true,
}
local valid_phone_temp = {"iːj", "uːj", "yːj", "ù", "ū", "ĵ"}
for _, temp in ipairs(valid_phone_temp) do
valid_phone[temp] = true
end
-- CONSONANT GROUPS --
local consonant = {
["b"] = true, ["ç"] = true, ["d"] = true, ["ð"] = true, ["dz"] = true,
["dʒ"] = true, ["f"] = true, ["ɡ"] = true, ["ɣ"] = true, ["h"] = true,
["j"] = true, ["k"] = true, ["l"] = true, ["m"] = true, ["n"] = true,
["ŋ"] = true, ["p"] = true, ["r"] = true, ["s"] = true, ["ʃ"] = true,
["t"] = true, ["ts"] = true, ["tʃ"] = true, ["v"] = true, ["w"] = true,
["z"] = true, ["ʒ"] = true, ["θ"] = true,
-- allophones --
["x"] = true, ["ʝ"] = true,
}
local consonant_temp = {"ĵ"}
for _, temp in ipairs(consonant_temp) do
consonant[temp] = true
end
local obstruent = {
["b"] = true, ["ç"] = true, ["d"] = true, ["ð"] = true, ["dz"] = true,
["dʒ"] = true, ["f"] = true, ["ɡ"] = true, ["ɣ"] = true, ["h"] = true,
["k"] = true, ["p"] = true, ["s"] = true, ["ʃ"] = true,
["t"] = true, ["ts"] = true, ["tʃ"] = true, ["v"] = true,
["z"] = true, ["ʒ"] = true, ["θ"] = true,
-- allophones --
["x"] = true,
}
local obstruent_voiced = {
["b"] = true, ["ç"] = false, ["d"] = true, ["ð"] = true, ["dz"] = true,
["dʒ"] = true, ["f"] = false, ["ɡ"] = true, ["ɣ"] = true, ["h"] = false,
["k"] = false, ["p"] = false, ["s"] = false, ["ʃ"] = false,
["t"] = false, ["ts"] = false, ["tʃ"] = false, ["v"] = true,
["z"] = true, ["ʒ"] = true, ["θ"] = false,
}
local obstruent_devoice = {
["b"] = "p", ["ç"] = "ç", ["d"] = "t", ["ð"] = "θ", ["dz"] = "ts",
["dʒ"] = "tʃ", ["f"] = "f", ["ɡ"] = "k", ["ɣ"] = "h", ["h"] = "h",
["k"] = "k", ["p"] = "p", ["s"] = "s", ["ʃ"] = "ʃ",
["t"] = "t", ["ts"] = "ts", ["tʃ"] = "tʃ", ["v"] = "f",
["z"] = "s", ["ʒ"] = "ʃ", ["θ"] = "θ",
-- allophones --
["x"] = "x",
}
local obstruent_voice = {
["b"] = "b", ["ç"] = "j", ["d"] = "d", ["ð"] = "ð", ["dz"] = "dz",
["dʒ"] = "dʒ", ["f"] = "v", ["ɡ"] = "ɡ", ["ɣ"] = "ɣ", ["h"] = "h",
["k"] = "ɡ", ["p"] = "b", ["s"] = "z", ["ʃ"] = "ʒ",
["t"] = "d", ["ts"] = "dz", ["tʃ"] = "dʒ", ["v"] = "v",
["z"] = "z", ["ʒ"] = "ʒ", ["θ"] = "ð",
-- allophones --
["x"] = "ɣ",
}
local affricate = {
["dz"] = true, ["dʒ"] = true ,
["ts"] = true, ["tʃ"] = true,
}
local sibilant = {
["dz"] = true, ["dʒ"] = true ,
["s"] = true, ["ʃ"] = true,
["ts"] = true, ["tʃ"] = true,
["z"] = true, ["ʒ"] = true,
}
local sibilant_alv = {
["dz"] = true,
["s"] = true,
["ts"] = true,
["z"] = true,
}
local sibilant_post = {
["dʒ"] = true,
["ʃ"] = true,
["tʃ"] = true,
["ʒ"] = true,
}
local sibilant_alv_to_post = {
["dz"] = "dʒ",
["s"] = "ʃ",
["ts"] = "tʃ",
["z"] = "ʒ",
}
local nasal = {
["m"] = true, ["n"] = true, ["ŋ"] = true,
}
local glide = {
["j"] = true, ["w"] = true,
}
local glide_temp = {"ĵ"}
for _, temp in ipairs(glide_temp) do
glide[temp] = true
end
local iotate = {
["d"] = "dʒ", ["dz"] = "dʒ",
["ɡ"] = "j", ["ɣ"] = "j",
["h"] = "ç",
["k"] = "tʃ",
["l"] = "j",
["s"] = "ʃ",
["t"] = "tʃ", ["ts"] = "tʃ",
["z"] = "ʒ",
}
local Cv_fixed = { -- <Cv> combinations that uniformly pronounced without /v/ --
["b"] = true,
["f"] = true,
["p"] = true,
}
local Cv_split = { -- <Cv> combinations that are only pronounced with /v/ post-vocalically --
["m"] = true, ["n"] = true,
}
local Cr = { -- consonants that can precede /r/ in a medial onset --
["p"] = true, ["k"] = true, ["b"] = true, ["ɡ"] = true,
["ç"] = true, ["ð"] = true,
["f"] = true, ["ɣ"] = true, ["h"] = true,
["v"] = true,
["θ"] = true,
["t"] = true, ["d"] = true,
-- allophones --
["x"] = true,
}
local Cl = { -- consonants that can precede /l/ in a medial onset --
["p"] = true, ["k"] = true, ["b"] = true, ["ɡ"] = true,
["ç"] = true, ["ð"] = true,
["f"] = true, ["ɣ"] = true, ["h"] = true,
["v"] = true,
["θ"] = true,
["s"] = true, ["ʃ"] = true, ["z"] = true, ["ʒ"] = true,
-- allophones --
["x"] = true,
}
local Cv = { -- consonants that can precede /v/ in a medial onset --
["ç"] = true, ["d"] = true, ["ð"] = true, ["dz"] = true,
["dʒ"] = true, ["ɡ"] = true, ["ɣ"] = true, ["h"] = true,
["k"] = true, ["l"] = true, ["r"] = true, ["s"] = true, ["ʃ"] = true,
["t"] = true, ["ts"] = true, ["tʃ"] = true,
["z"] = true, ["ʒ"] = true, ["θ"] = true,
-- allophones --
["x"] = true, ["ʝ"] = true,
}
local continuant = { -- consonants that can precede nasals in a medial onset -- (non-glide continuants)
["ç"] = true, ["ð"] = true,
["f"] = true, ["ɣ"] = true, ["h"] = true,
["l"] = true, ["r"] = true, ["s"] = true, ["ʃ"] = true,
["v"] = true,
["z"] = true, ["ʒ"] = true, ["θ"] = true,
-- allophones --
["x"] = true,
}
local CN = continuant
-- VOWEL GROUPS --
local vowel = {
["a"] = true, ["aː"] = true, ["aːː"] = true, ["æː"] = true, ["ai"] = true, ["au"] = true,
["eː"] = true, ["ei"] = true, ["ɛ"] = true, ["ɤ"] = true, ["ɤi"] = true, ["i"] = true,
["iː"] = true, ["iːː"] = true, ["iːe"] = true, ["ia"] = true, ["ie"] = true, ["oː"] = true,
["œ"] = true, ["œa"] = true, ["øi"] = true, ["ɔ"] = true, ["ɔː"] = true, ["ɔa"] = true,
["ɔi"] = true, ["u"] = true, ["uː"] = true, ["uːː"] = true, ["uːo"] = true, ["ua"] = true,
["uo"] = true, ["y"] = true,
}
local vowel_temp = {"iːj", "uːj", "yːj", "ù", "ū"}
for _, temp in ipairs(vowel_temp) do
vowel[temp] = true
end
local long = {
["a"] = false, ["aː"] = true, ["aːː"] = true, ["æː"] = true, ["ai"] = true, ["au"] = true,
["eː"] = true, ["ei"] = true, ["ɛ"] = false, ["ɤ"] = false, ["ɤi"] = true, ["i"] = false,
["iː"] = true, ["iːː"] = true, ["iːe"] = true, ["ia"] = true, ["ie"] = true, ["oː"] = true,
["œ"] = false, ["œa"] = true, ["øi"] = true, ["ɔ"] = false, ["ɔː"] = true, ["ɔa"] = true,
["ɔi"] = true, ["u"] = false, ["uː"] = true, ["uːː"] = true, ["uːo"] = true, ["ua"] = true,
["uo"] = true, ["y"] = false,
}
local weak = {
["a"] = false, ["aː"] = false, ["aːː"] = false, ["æː"] = false, ["ai"] = false, ["au"] = false,
["eː"] = false, ["ei"] = false, ["ɛ"] = false, ["ɤ"] = true, ["ɤi"] = false, ["i"] = true,
["iː"] = false, ["iːː"] = false, ["iːe"] = false, ["ia"] = false, ["ie"] = false, ["oː"] = false,
["œ"] = false, ["œa"] = false, ["øi"] = false, ["ɔ"] = false, ["ɔː"] = false, ["ɔa"] = false,
["ɔi"] = false, ["u"] = false, ["uː"] = false, ["uːː"] = false, ["uːo"] = false, ["ua"] = false,
["uo"] = false, ["y"] = false,
}
-- MISC --
local boundary = {
[" "] = true, ["|"] = true, ["·"] = true,
}
-- PROCESS FUNCTIONS --
local IPA = {}
function generate_IPA(word)
local s = mw.ustring.lower(word)
-- Replace irregulars --
for toReplace, ReplaceKey in pairs(irregular) do
if mw.ustring.match(s, toReplace) then
s = mw.ustring.gsub(s, toReplace, ReplaceKey)
-- generate_IPA: mw.log("Irregular spelling <" .. toReplace .. "> recognised. Treating as <" .. ReplaceKey .. ">.")
end
end
local s_len = mw.ustring.len(s)
IPA = {}
local split_s = {}
for i = 1, s_len do
split_s[i] = mw.ustring.sub(s, i,i)
end
-- generate_IPA: mw.log("————— BEGINNING BASE GENERATION —————")
if s_len == 0 then
error("Empty input.")
end
while s_len > 0 do
local getData = {}
local multiMatch = false
local i_iteration = -2
-- generate_IPA: mw.log("=========================\nCURRENT TEST STRING: <".. mw.ustring.upper(s) .. ">")
if s_len < 3 then
i_iteration = 1 - s_len
end
for i = i_iteration, 0 do
-- generate_IPA: mw.log("————— <" .. split_s[s_len + i] .. "> selected. (i = " .. i .. ") —————")
getData = data[split_s[s_len + i]]
local deadEnd = false
if data[split_s[s_len + i]] == nil then
error("'" .. split_s[s_len + i] .. "' is an invalid character.")
end
while type(getData) == "table" do
if i == 0 then
if getData[false] then
-- generate_IPA: mw.log("Singular index recognised.")
getData = getData[false]
-- generate_IPA: mw.log("Index acquired: " .. getData)
else
error(split_s[s_len] .. " is an invalid character.")
end
else
-- generate_IPA: mw.log("Tabular index recognised.")
for j = 1, 0 - i do
local currentCombo = ""
-- for logs only
if i == -2 and j == 2 then
currentCombo = split_s[s_len + i + j - 2] .. " + " .. split_s[s_len + i + j - 1] .. " + " .. split_s[s_len + i + j]
else
currentCombo = split_s[s_len + i + j - 1] .. " + " .. split_s[s_len + i + j]
end
-- generate_IPA: mw.log("Testing " .. currentCombo)
--
if getData[split_s[s_len + i + j]] then
getData = getData[split_s[s_len + i + j]]
-- generate_IPA: mw.log("Combination recognised: " .. currentCombo .. " (j = " .. j .. ")")
if type(getData) == "string" then
if j + i == 0 then
-- generate_IPA: mw.log("Index acquired: " .. getData)
multiMatch = true
break
else
-- generate_IPA: mw.log('Non-final index: dead end.')
getData = {}
deadEnd = true
break
end
elseif j + i == 0 and getData[false] then
getData = getData[false]
-- generate_IPA: mw.log("Index acquired: " .. getData)
multiMatch = true
break
elseif j + i == 0 and not getData[false] then
error("data[" .. table.concat(getData, "][") .. "][false] is missing." )
else
-- generate_IPA: mw.log("Target still tabular: reiterating.")
end
else
-- generate_IPA: mw.log('Dead end.')
getData = {}
deadEnd = true
break
end
end
if type(getData) == "table" then break end
end
end
if type(getData) == "string" and (i == 0 or multiMatch == true) then
-- generate_IPA: mw.log("Target acquired of length " .. 1 - i .. ", converting to [" .. getData .. "].")
table.insert(IPA, 1, getData)
s = mw.ustring.sub(s, 1, s_len + i - 1)
s_len = mw.ustring.len(s)
break
elseif deadEnd == false then
-- generate_IPA: mw.log('Non-final index: dead end.')
end
end
end
-- generate_IPA: mw.log('————— STRING EXHAUSTED —————')
mw.log("Base generation result: [" .. table.concat(IPA,"][") .. "]")
return IPA
end
function resolve_vowels(phones)
local working_phones = phones
mw.log("————— BEGINNING VOWEL RESOLUTION —————")
local i = 0
while true do
i = i + 1
local p_prev = working_phones[i - 1]
local p_current = working_phones[i]
local p_next = working_phones[i + 1]
local p_next2 = working_phones[i + 2]
local p_next3 = working_phones[i + 3]
local toResolve = false
if p_current == nil then break end
local function p_Resolve(p_new)
working_phones[i] = p_new
mw.log("[" .. p_current .. "] resolved to [" .. p_new .. "] in position ".. i .. ".")
p_new = ""
end
local function p_Convert(p_new)
working_phones[i] = p_new
mw.log("[" .. p_current .. "] converted to [" .. p_new .. "] in position ".. i .. ".")
p_current = p_new
p_new = ""
end
-- Resolution of [aù] --
if p_prev == "a" and p_current == "ù" then
mw.log("<aù> recognised in position " .. i .. ". Converting to resolvable [u].")
p_Convert("u")
p_current = "u"
end
-- Resolution of [u], [ù] and [ū] --
if p_current == "u" then
mw.log("[u] found in position " .. i .. ". Testing for resolution.")
if consonant[p_next] then
if glide[p_next2] then
if not vowel[p_next3] then
mw.log("ɤCj!V environment identified.")
p_Resolve("ɤ")
else
mw.log("No resolution needed.")
end
elseif not vowel[p_next2] and not glide[p_next2] then
mw.log("ɤC!V environment identified.")
p_Resolve("ɤ")
else
mw.log("No resolution needed.")
end
else
mw.log("No resolution needed.")
end
elseif p_current == "ù" or p_current == "ū" then
mw.log("Fixed [u] found in position " .. i .. ".")
working_phones[i] = "u"
end
if toResolve == true then
working_phones[i] = "ɤ"
mw.log("[u] → [ɤ] in position ".. i .. ".")
end
-- Resolution of <ei> and <øi> --
if p_current == "ei" then
for j = 1, i do
local check_phone = working_phones[i - j]
if boundary[check_phone] or check_phone == nil then
mw.log("Initial [ei] found in position" .. i .. ".")
p_Resolve("ai")
break
elseif not consonant[check_phone] then
break
end
end
end
if vowel[p_next] then
-- Resolution of prevocalic <iej>, <uoj> and <yej> --
if p_current == "iej" or p_current == "yej" or p_current == "uoj" then
mw.log("Pre-vocalic <" .. p_current .. "> found in position " .. i .. ".")
if p_current == "uoj" then
p_Resolve("uo")
table.insert(working_phones, i + 1, "j")
mw.log("[j] inserted to position " .. i + 1 .. ".")
else
p_Resolve("iː")
end
end
-- Resolution of prevocalic [ie] and [uo] --
if p_current == "ie" then
p_Resolve("iː")
elseif p_current == "uo" then
p_Resolve("uː")
end
else
-- Resolution of non-prevocalic <iej>, <uoj> and <yej> --
for toResolve, ResolveKey in pairs({["iej"] = "ei", ["uoj"] = "ɔi", ["yej"] = "øi"}) do
if p_current == toResolve then
p_Resolve(ResolveKey)
end
end
end
end
-- Removes placeholder phones from data --
for _, temp in ipairs(vowel_temp) do
valid_phone[temp] = nil
vowel[temp] = nil
end
mw.log("Vowel resolution result: [" .. table.concat(working_phones,"][") .. "]")
return working_phones
end
function resolve_consonants(phones, hj_dv, phon)
local working_phones = phones
mw.log("————— BEGINNING CONSONANT RESOLUTION —————")
local i = 0
while true do
i = i + 1
local p_prev = working_phones[i - 1]
local p_current = working_phones[i]
local p_next = working_phones[i + 1]
local p_next2 = working_phones[i + 2]
local p_next3 = working_phones[i + 3]
local toResolve = false
if p_current == nil then break end
local function p_Resolve(p_new)
working_phones[i] = p_new
mw.log("[" .. p_current .. "] resolved to [" .. p_new .. "] in position ".. i .. ".")
p_new = ""
end
local function p_Convert(p_new)
working_phones[i] = p_new
mw.log("[" .. p_current .. "] converted to [" .. p_new .. "] in position ".. i .. ".")
p_current = p_new
p_new = ""
end
local function p_RemoveNext()
table.remove(working_phones, i + 1)
p_next = working_phones[i + 1]
p_next2 = working_phones[i + 2]
p_next3 = working_phones[i + 3]
end
-- Resolution of iotation --
if iotate[p_current] and p_next == "j" then
mw.log("[" .. p_current .. "][j] found in position " .. i .. ".")
p_Convert(iotate[p_current])
p_RemoveNext()
mw.log("[j] removed from position " .. i + 1 .. ".")
elseif p_current == "ĵ" then
p_Convert("j")
end
-- Resolution of hjádvanþs --
if not hj_dv then
if p_current == "ç" then
mw.log("hj = false:")
p_Convert("ʃ")
elseif p_current == "j" and (boundary[p_prev] or p_prev == nil) and consonant[p_next] then
mw.log("hj = false:")
p_Convert("ʒ")
end
end
-- Resolution of (T)Ss, (T)Sș, ts, ds, tș, dș (progressive voicing assimilation) --
if p_next == "s" then
if sibilant[p_current] then
p_RemoveNext()
mw.log("[s] removed from position " .. i + 1 .. " following a sibilant.")
elseif p_current == "t" then
p_Convert("ts")
p_RemoveNext()
mw.log("[t][s] → [ts] in position " .. i .. ".")
elseif p_current == "d" then
p_Convert("dz")
p_RemoveNext()
mw.log("[d][s] → [dz] in position " .. i .. ".")
end
elseif p_next == "ʃ" then
if sibilant_post[p_current] == true then
p_RemoveNext()
mw.log("[ʃ] removed from position " .. i + 1 .. " following a postalveolar sibilant.")
elseif sibilant_alv[p_current] then
mw.log("[" .. p_current .. "][ʃ] → [" .. sibilant_alv_to_post[p_current] .. "] in position " .. i .. ".")
p_Convert(sibilant_alv_to_post[p_current])
p_RemoveNext()
elseif p_current == "t" then
p_Convert("tʃ")
p_RemoveNext()
mw.log("[t][ʃ] → [tʃ] in position " .. i .. ".")
elseif p_current == "d" then
p_Convert("dʒ")
p_RemoveNext()
mw.log("[d][ʃ] → [dʒ] in position " .. i .. ".")
end
end
-- Resolution of Cv --
if p_next == "v" and ((Cv_split[p_current] and not vowel[p_prev]) or Cv_fixed[p_current]) then
mw.log("Cluster [" .. p_current .. "][v] resolved to [" .. p_current .. "] at position " .. i .. ".")
p_RemoveNext()
end
-- Resolution of regressive voicing assimilation --
if obstruent[p_current] == true then
mw.log("Obstruent [" .. p_current .. "] found in position " .. i .. ". Searching for cluster.")
local final_i = i
-- find voicing of final obstruent in cluster --
for j = i + 1, #working_phones do
local check_phone = working_phones[j]
if obstruent[check_phone] == true and not check_phone == "v" then -- /v/ is excluded
final_i = j
else break
end
end
if final_i > i then -- if cluster recognised --
final_obs = working_phones[final_i]
mw.log("Final obstruent in cluster is [" .. final_obs .. "], cluster length " .. final_i - i + 1 .. ".")
-- assimilate --
if not obstruent_voiced[p_current] == obstruent_voiced[final_obs] then
mw.log("Voicing mismatch found in cluster at position " .. i .. ".")
if obstruent_voiced[final_obs] == true then
p_Convert(obstruent_voice[p_current])
else
p_Convert(obstruent_devoice[p_current])
end
else
mw.log("No voicing mismatch found.")
end
else
mw.log("No cluster found.")
end
end
-- Progressive voicing assimilation of Cv --
if p_current == "v" and not obstruent_voiced[p_prev] then
p_Resolve("f")
mw.log("[" .. p_current .. "][v] → [" .. p_current .. "][f] in position " .. i .. ".")
end
-- Resolution of geminates --
if p_next == p_current and (boundary[p_next2] or p_next2 == nil) and consonant[p_current] then
p_RemoveNext()
mw.log("Geminate [" .. p_current .. "] removed in final position at position " .. i .. ".")
end
-- PHONETIC RESOLUTION --
if phon == true then
if p_current == "h" and not vowel[p_next] then
mw.log("phon = true:")
p_Resolve("x")
elseif p_current == "h" and p_next == "j" then
mw.log("phon = true:")
p_Resolve("ç")
p_RemoveNext()
end
end
end
-- Removes placeholder phones from data --
for _, temp in ipairs(consonant_temp) do
valid_phone[temp] = nil
consonant[temp] = nil
glide[temp] = nil
end
mw.log("Consonant resolution result: [" .. table.concat(working_phones,"][") .. "]")
return working_phones
end
function get_syllables(phones)
local working_phones = phones
local syllables = {}
local working_syllable = {
["onset"] = {},
["nucleus"] = "",
["coda"] = {},
}
local syllable_no = 1
local function logSyllable()
local onset = ""
local coda = ""
if working_syllable["onset"] then
onset = table.concat(working_syllable["onset"], " ")
end
if working_syllable["coda"] then
coda = table.concat(working_syllable["coda"], " ")
end
mw.log( onset .. " [ " .. working_syllable["nucleus"] .. " ] " .. coda )
end
mw.log("————— BUILDING SYLLABLES —————")
-- division into syllables --
while #working_phones > 0 do
local p_current = working_phones[1]
local p_next = working_phones[2]
local p_next2 = working_phones[3]
local p_next3 = working_phones[4]
local function register_syllable(check)
if check == true then
syllables[syllable_no] = working_syllable
if #syllables[syllable_no]["onset"] == 0 then
syllables[syllable_no]["onset"] = nil
end
if #syllables[syllable_no]["coda"] == 0 then
syllables[syllable_no]["coda"] = nil
end
mw.log("Syllable " .. syllable_no .. " registered:")
logSyllable()
mw.log("====================")
syllable_no = syllable_no + 1
working_syllable = {
["onset"] = {},
["nucleus"] = "",
["coda"] = {},
}
end
end
local function register_boundary(check)
if check == true then
register_syllable(true)
syllables[syllable_no] = p_current
table.remove(working_phones, 1)
mw.log("Boundary syllable " .. syllable_no .. " registered: '" .. p_current .. "'\n====================")
syllable_no = syllable_no + 1
end
end
local function register_phone(check)
if check == true then
if vowel[p_current] then
working_syllable["nucleus"] = p_current
mw.log("[" .. p_current .. "] is now the working nucleus.")
elseif consonant[p_current] then
local unit = ""
if working_syllable["nucleus"] == "" then
unit = "onset"
else
unit = "coda"
end
table.insert(working_syllable[unit], p_current)
mw.log("[" .. p_current .. "] appended to the working " .. unit .. ".")
else
error("Invalid input for function register_phone")
end
table.remove(working_phones, 1)
-- mw.log("Current working syllable:")
-- logSyllable()
if #working_phones == 0 then
register_syllable(true)
end
end
end
if consonant[p_current] then
if working_syllable["nucleus"] == "" then
register_phone(true)
else
if vowel[p_next] or (glide[p_next] and vowel[p_next2]) then -- *C*(J)V
register_syllable(true)
register_phone(true)
elseif #working_syllable["coda"] > 0 then -- C | …
local function JV(check)
if check == true then
if vowel[p_next2] or (glide[p_next2] and vowel[p_next3]) then -- C | *C*l(J)V
register_syllable(true)
register_phone(true)
else
register_phone(true)
end
end
end
if Cl[p_current] and p_next == "l" then -- C | *C*l …
JV(true) -- C | *C*l(J)V
elseif Cr[p_current] and p_next == "r" then -- C | *C*r …
JV(true) -- C | *C*r(J)V
elseif Cv[p_current] and (p_next == "v" or p_next == "f") then -- C | *C*v …
JV(true) -- C | *C*v(J)V
elseif CN[p_current] and nasal[p_next] then -- C | *C*N …
JV(true) -- C | *C*N(J)V
else
register_phone(true)
end
else
register_phone(true)
end
end
elseif vowel[p_current] then
if working_syllable["nucleus"] == "" then
register_phone(true)
else
register_syllable(true)
register_phone(true)
end
elseif boundary[p_current] or p_current == "-" then
register_boundary(true)
else
error("Unrecognised phone in syllabifier: " .. p_current)
end
end
mw.log("STRING EXHAUSTED\n====================")
return syllables
end
function tag_syllables(syllables)
mw.log("————— TAGGING SYLLABLES —————")
local syl_count = #syllables
for i = 1, syl_count do
local syllable = syllables[i]
local function tag_syllable(tag, value)
syllables[i][tag] = value
mw.log("syllables[" .. i .. "][" .. tag .. "] = " .. tostring(value) )
end
-- tag checked syllables --
if type(syllable) == "table" then
if syllable["coda"] == nil then
tag_syllable("checked", false)
else
tag_syllable("checked", true)
end
-- tag long syllables --
if long[syllable["nucleus"]] then
tag_syllable("long", true)
else
tag_syllable("long", false)
end
-- tag weak syllables (nucleus = [i] [ɤ]) --
if weak[syllable["nucleus"]] then
tag_syllable("weak", true)
else
tag_syllable("weak", false)
end
-- tag heavy syllables (nucleus is long coda is an obstruent-initial cluster) -
if syllable["coda"] then
if long[syllable["nucleus"]] and affricate[syllable["coda"][1]] then
tag_syllable("heavy", true)
elseif long[syllable["nucleus"]] and obstruent[syllable["coda"][1]] and syllable["coda"][2] then
tag_syllable("heavy", true)
else
tag_syllable("heavy", false)
end
end
mw.log("—————")
end
end
return syllables
end
function get_stress(syllables)
--[[
Stress in Radestrian is calculated excluding · prefixes, which are stressed as if they were standalone words but with secondary stress. Stress in Radestrian falls on either the first syllable or the second syllable. The first syllable is the default stress position. Second-syllable stress occurs in eight circumstances:
1:
σₙ = 2
σ₁: short, open
σ₂: heavy
2:
σₙ = 3
σ₁: short, open
σ₂: long
3:
σₙ = 3
σ₁: short, closed
σ₂: long, closed
4:
σₙ = 3
σ₁: short
σ₂: short, not weak, closed
5:
σₙ = 4+
σ₁: short, open
σ₂: short, open
σ₃: short
6:
σₙ = 4+
σ₁: short, open
σ₂: short, closed
7:
σₙ = 4+
σ₁: short, open
σ₂: long
8:
σₙ = 4+
σ₁: short, closed
σ₄: short
]]
local working_word = {}
local output = {}
local test_n = #syllables
mw.log("————— BEGINNING STRESS ASSIGNMENT —————")
while #syllables > 0 do
-- basically an iteration of working words
if type(syllables[1]) == "table" then -- testing for boundary syllable
-- set up current working word by adding and removing the initial syllables from the array until a boundary is registered
for i = 1, test_n do
table.insert(working_word, syllables[1])
table.remove(syllables, 1)
mw.log("Syllable " .. i .. " registered to working word.")
if syllables[1] == nil or type(syllables[1]) == "string" then -- check next syllable is a boundary (now 1 after deletion of registered syllable)
mw.log("Boundary found: continuing to stress assignment.")
break
end -- if next syllable is a boundary, register and continue to stress assignment
end
-- INTERNAL FUNCTIONS
local function stress(syl) -- assigns primary or secondary stress to the current working word
if syl == 1 or syl == 2 then
if syllables[1] == "-" or output[#output] == "·" then -- check if following · or preceding - ; if so, assign stress regardless of monosyllabicness
working_word[syl]["stress"] = true
mw.log("Primary stress assigned to syllable " .. syl .. ".")
elseif #working_word > 1 then -- if monosyllabic, do not add a redundant stress mark
if output[#output] == "-" or syllables[1] == "·" then -- check if following - or preceding ·
working_word[syl]["stress2"] = true
mw.log("Secondary stress assigned to syllable " .. syl .. ".")
else
working_word[syl]["stress"] = true
mw.log("Primary stress assigned to syllable " .. syl .. ".")
end
else
mw.log("Implicit primary stress assigned to monosyllable.")
end
-- register the working word
if #working_word > 0 then
for j = 1, #working_word do
table.insert(output, working_word[j])
end
working_word = {}
mw.log("Word registered to output.")
else
error("An empty word cannot be registered.")
end
else
error("Empty or invalid input to stress().")
end
end
-- calculate stress on the working word
mw.log("Syllables in working word: " .. #working_word)
if #working_word == 0 or working_word == nil then
error("The working word is empty.")
elseif #working_word == 1 then -- monosyllabic?
stress(1)
elseif working_word[1]["long"] then
stress(1)
elseif #working_word == 2 then -- disyllabic?
if working_word[2]["heavy"] then -- condition 1
stress(2)
else
stress(1)
end
elseif #working_word == 3 then -- trisyllabic?
if not working_word[2]["weak"] and working_word[2]["checked"] then -- condition 4
stress(2)
elseif working_word[2]["long"] then
if working_word[1]["checked"] then
if working_word[2]["checked"] then -- condition 3
stress(2)
else
stress(1)
end
elseif working_word[2]["long"] then -- condition 2
stress(2)
else
stress(1)
end
else
stress(1)
end
elseif #working_word >= 4 then -- 4+ syllables
if working_word[1]["checked"] then
if not working_word[3]["long"] then -- condition 8
stress(2)
else
stress(1)
end
elseif working_word[2]["long"] then -- condition 7
stress(2)
elseif working_word[2]["checked"] then -- condition 6
stress(2)
elseif not working_word[3]["long"] then -- condition 5
stress(2)
else
stress(1)
end
else
error("Invalid syllable count.")
end
elseif syllables[1] == nil then
error("Empty syllable found in array. The array should contain no empty syllables.")
else -- if boundary is identified as the working word, then
mw.log ("Boundary syllable identified.")
table.insert(output, syllables[1])
mw.log("Boundary registered to output: [" .. syllables[1] .. "]")
table.remove(syllables, 1)
end
mw.log("—————")
end
mw.log("SYLLABLES EXHAUSTED\n==========\nRemoving [·] and [-] from the system.")
local screened = false
while screened == false do
for i = 1, #output do
if output[i] == "-" or output[i] == "·" then
table.remove(output, i)
break
elseif i == #output then
screened = true
end
end
end
return output
end
function resolve_syllables(syllables)
mw.log("————— RESOLVING SYLLABLES —————")
if #syllables > 1 then
for i = 1, #syllables - 1 do
local working_cluster = {}
if (syllables[i]["coda"] or syllables[i+1]["onset"]) and syllables[i+1] and type(syllables[i]) == "table" and type(syllables[i+1]) == "table" then
-- mw.log("Resolving clusters in syllables " .. i .. " and " .. i + 1 .. ".")
-- get intersyllabic clusters --
if syllables[i]["coda"] then
for j = 1, #syllables[i]["coda"] do
table.insert(working_cluster, syllables[i]["coda"][j])
end
end
if syllables[i+1]["onset"] then
for j = 1, #syllables[i+1]["onset"] do
table.insert(working_cluster, syllables[i+1]["onset"][j])
end
end
-- mw.log("Cluster to be resolved: " .. table.concat(working_cluster))
local p_last = working_cluster[#working_cluster]
local p_last2 = working_cluster[#working_cluster-1]
local p_last3 = working_cluster[#working_cluster-2]
local p_last4 = working_cluster[#working_cluster-3]
local new_coda = {}
local new_onset = {}
-- splits the current cluster into new coda and new onset and re-assigns them to the syllables --
local function split(point)
if point < 1 or point > 3 or point == nil then
error("split() must take integer values between 1 to 4 inclusive")
end
for j = #working_cluster + 1 - point, #working_cluster do
table.insert(new_onset, working_cluster[j])
end
if #working_cluster - point > 0 then
for j = 1, #working_cluster - point do
table.insert(new_coda, working_cluster[j])
end
end
mw.log("σ" .. i .. " | σ" .. i+1 .. " : … " .. table.concat(new_coda) .. " | " .. table.concat(new_onset) .. " …")
syllables[i]["coda"] = new_coda
syllables[i+1]["onset"] = new_onset
end
-- determine syllable boundary --
if glide[p_last] and nasal[p_last2] and CN[p_last3] and p_last4 then -- C|CNJ
split(3)
elseif nasal[p_last] and CN[p_last2] and p_last3 then -- C|CN
split(2)
elseif glide[p_last] and p_last2 == "r" and Cr[p_last3] and p_last4 then -- C|CrJ
split(3)
elseif p_last == "r" and Cr[p_last2] and p_last3 then -- C|Cr
split(2)
elseif glide[p_last] and p_last2 == "l" and Cl[p_last3] and p_last4 then -- C|ClJ
split(3)
elseif p_last == "l" and Cl[p_last2] and p_last3 then -- C|Cl
split(2)
elseif glide[p_last] and (p_last2 == "v" or p_last2 == "f") and Cv[p_last3] and p_last4 then -- C|CvJ
split(3)
elseif (p_last == "v" or p_last == "f") and Cv[p_last2] and p_last3 then -- C|Cv
split(2)
elseif glide[p_last] and p_last2 then -- C|CJ
split(2)
else -- C|C
split(1)
end
end
end
end
return syllables
end
function syllables_to_string(syllables)
local output = ""
for i = 1, #syllables do
local onset = ""
local coda = ""
local stress=""
if type(syllables[i]) == "table" then
if syllables[i]["onset"] then
onset = table.concat(syllables[i]["onset"])
end
if syllables[i]["coda"] then
coda = table.concat(syllables[i]["coda"])
end
if syllables[i]["stress"] then
stress = "ˈ"
end
if syllables[i]["stress2"] then
stress = "ˌ"
end
output = output .. stress .. onset .. syllables[i]["nucleus"] .. coda
else
output = output .. syllables[i]
end
end
return output
end
function export.generate(frame)
local args = getArgs(frame)
local outputIPA = generate_IPA(args[1])
local parameters = {}
local p = 2
mw.log("——— Parameters ———")
while args[p] do
parameters[args[p]] = true
mw.log(args[p] .. " = true")
p = p + 1
end
outputIPA = resolve_vowels(outputIPA)
outputIPA = resolve_consonants(outputIPA, parameters["hj"], parameters["phon"])
outputIPA = get_syllables(outputIPA)
outputIPA = tag_syllables(outputIPA)
outputIPA = get_stress(outputIPA)
outputIPA = resolve_syllables(outputIPA)
outputIPA = syllables_to_string(outputIPA)
mw.log(outputIPA)
return outputIPA
end
return export
--[[
Debug console test string:
=p.generate(mw.getCurrentFrame():newChild{title="whatever",args={"rjaovs"}})
=p.generate(mw.getCurrentFrame():newChild{title="whatever",args={"rjaovs", "true"}}) (for hjádvanþs)
]]