Module:nv-conj

--[[ Based on Kari's paper (1978) with several personal additions, adaptations and fixes.

NOTES: - Lua regex are very expensive, had to scale back my all-regex coding paradigm. - In order to facilitate usage of Lua regex (which are very limited), consonants are converted to a one-symbol format (ex: q for k', č for ch...), then rules are applied, then the word is converted back to conventional orthography.

TODO: - seriative, - "make" verbs in the perfective, - get rid of the ":" altogether as a formative boundary. ]]

local p = {}

local umatch= mw.ustring.match local ufind = mw.ustring.find local subst = mw.ustring.gsub

local UTF8_char = "[\1-\127\194-\244][\128-\191]*"

-- forward declarations local convert, fut_adjustment_conj

-- ENTRY POINTS function p.show(frame) return p.conj(frame:getParent) end

function p.conj(frame) local f = frame.args

-- retrieve parameters get_params(frame) local data = {} data.form_1sg = f["1sg"] or proc("1sg") data.form_2sg = f["2sg"] or proc("2sg") data.form_3sg = f["3sg"] or proc("3sg") data.form_4sg = f["4sg"] or proc("4sg") data.form_1dl = f["1dl"] or proc("1dl") data.form_2dl = f["2dl"] or proc("2dl") data.form_1pl = f["1pl"] or proc("1pl") data.form_2pl = f["2pl"] or proc("2pl") data.form_3pl = f["3pl"] or proc("3pl") data.form_4pl = f["4pl"] or proc("4pl")

if frame.args["test"] then return make_string(data) else return make_table(data) end end

function get_params(frame)

-- convert 7-args mode to 4-args mode if frame.args[7] then frame.args["conj"]=frame.args[4] frame.args["obj"] =frame.args[3] frame.args["disj"]=frame.args[2] frame.args[4]    =frame.args[7] frame.args[3]    =frame.args[6] frame.args[2]    =frame.args[5] end

mode_ = frame.args[1] asp_ = frame.args[2] cl_  = frame.args[3] stem_ = frame.args[4] disj_ = frame.args["disj"] or "" conj_ = frame.args["conj"] or "" obj_ = frame.args["obj"]  or ""

neuter_= frame.args["neuter"] -- ni / ∅ nodevoice_= frame.args["nodevoice"] d_effect_= frame.args["d-effect"] nospread_= frame.args["nospread"] dashift_= frame.args["dashift"] hop_   = frame.args["hop"] or ""

-- underlying representations -- TODO: improve below code --      manage multiple disj/conj prefs

if disj_ == "a" then disj_ = "ʼi" elseif disj_ == "á" then disj_ = "ʼí" --elseif disj_ == "á" then disj_ = "ʼá" end

disj_ = subst(disj_, "kó", "kwí")

if obj_ == "a" then obj_ = "ʼi" elseif obj_ == "y" then obj_ = "yi" elseif obj_ == "ho" then obj_="hwi" end fut_="" if mode_=="fut" then fut_adjustment_conj end

disj_ = convert(disj_) obj_ = convert(obj_) conj_ = convert(conj_) stem_ = convert(stem_)

has_hiss = ufind(stem_, "[szcʒ]") has_hush = ufind(stem_, "[šžčćj]") end

-- PARADIGMS local paradigms={}

paradigms["impf"] = {} P = paradigms["impf"] P["∅"] ={  "š","ni",  "", "iid", "oh"} P["ni"] ={"niš","ní","ni","niid","noh"} P["yii"]={"iiš","ii","ii", "iid","ooh"} --P["í"] ={"ísh","íní","í", "íid", "óh"}

paradigms["perf"] = {} P = paradigms["perf"] P["yi"] ={"ğí", "ğíní","ğí","iid","oo"} P["yid"]={"ğiš","ğíní","ği","iid","ooh"}

P["ni"] ={"ní","ğíní","ní","niid","noo"} P["nid"]={"niš","ğíní","ni","niid","nooh"}

P["si"] ={"sé", "síní","z","siid","soo"} P["sid"]={"sis","síní","s","siid","sooh"} P["si2"] ={"é", "íní","ğez","eed","soo"} P["si2d"]={"és","íní","ğes","eed","sooh"}

P["yii"]={"ii","ini","ii", "iid","oo"} P["yiid"]={"iiš","ini","ii", "iid","ooh"}

paradigms["fut"] = {} P = paradigms["fut"] P["∅"] ={"ğeš", "ğí","ğo","ğiid","ğoh"} --P["∅"] ={"eeš", "íí","oo","iid","ooh"}

-- REGEXP local vowels = "aąáą́eęéę́iįíį́oǫóǫ́" local V = "[".. vowels.."]" local L = "[aąeęiįoǫ]" local H = "[áą́éę́íį́óǫ́]" local C = "[^:=#" .. vowels .."]" local C0= "[^=#" .. vowels .."]*=" local K = "[^:=#yʼ" .. vowels .."]" local I = "[ií]" local O = "[oó]" local S = "[sš]"

local high_tone = { ["a"] = "á", ["e"] = "é", ["i"] = "í", ["o"] = "ó" } local low_tone = { ["á"] = "a", ["é"] = "e", ["í"] = "i", ["ó"] = "o" }

local d_effect_map = { ["z"] = "ʒ",  ["ž"] = "j", ["ğ"] = "g",  ["y"] = "ʼy", ["w"] = "ʼw", ["l"] = "dl", ["ʼ"] = "tʼ", ["m"] = "ʼm", ["n"] = "ʼn" } --Note: stem must be provided with etymological y or gh, so as to elicit proper d-effect.

local devoicing = { ["z"] = "s", ["ğ"] = "h", ["ž"] = "š", ["l"] = "ł", ["w"] = "h", ["y"] = "h", }

local surds = "([sšhł])" local voiced = "([zžğwyl])"

local function at(str, i)	if i == 0 then return "" elseif i < 0 then local n = 0 local characters = {} for character in string.gmatch(str, UTF8_char) do			n = n + 1 characters[n] = character end return characters[n + i + 1] else local n = 0 for character in string.gmatch(str, UTF8_char) do			n = n + 1 if n == i then return character end end end

return "" end

local function assimilate(i, v)  v = at(v, 1) -- first vowel if long if i == "í" then return high_tone[v] or v  end

v = low_tone[v] or v  return subst(i, "i", v) end

local function split_pref(pref) cv,f = umatch(pref, "^(.-)([sšhd]*)$") return cv, f end

local function append(pref) if not pref or pref == "" then return end

if form_ ~= "" then last = at(form_, -1) if last == "#" or last == "=" then form_ = form_.. pref else form_ = form_.. ":".. pref end else form_ = pref end end

local abbrs = { V = V, H = H, L = L, C0 = C0, C = C, I = I, O = O, K = K, } local function expand_regex(str) return string.gsub(str, "[VHLCIOK]0?", abbrs)

--[[ expanding manually instead to speed up? Not faster actually...   ret=""   d=""   for c in mw.ustring.gmatch(str,".") do      if c == "V" then ret=ret..V     elseif c == "H" then ret=ret..H     elseif c == "L" then ret=ret..L     elseif d == "C" and c== "0" then          ret=ret..C0     elseif d == "C" and c ~= "0" then          ret=ret..C..c     elseif c == "I" then ret=ret..I     elseif c == "O" then ret=ret..O     elseif c == "K" then ret=ret..K     elseif c ~= "C" then ret=ret..c     end

d = c  end return ret ]] end

local function replace(str1, str2) str1 = expand_regex(str1) form_ = subst(form_, str1, str2) end

-- basic replace without wildcard local function replace2(str1, str2) form_ = subst(form_, str1, str2) end

local function match(str2) str2 = expand_regex(str2) return umatch(form_, str2) end

-- basic replace without wildcard local function match2(str2) return umatch(form_, str2) end

di2mono = { ["kw"] = "q", ["hw"] = "f", ["kʼ"] = "ç", ["čʼ"] = "ć",  -- trick ["tʼ"] = "ť", ["ts"] = "c", ["sh"] = "š", ["zh"] = "ž", ["gh"] = "ğ", ["ch"] = "č", ["dz"] = "ʒ" } mono2di = { ["q"] = "kw", ["f"] = "hw", ["ç"] = "kʼ", ["ć"] = "chʼ", ["ť"] = "tʼ", ["c"] = "ts", ["š"] = "sh", ["ž"] = "zh", ["ğ"] = "gh", ["č"] = "ch", ["ʒ"] = "dz", ["ñ"] = "n" }

-- declared as local above function convert(str) -- using regex only is too expensive -- doing it manually ret = "" d = "" for c in mw.ustring.gmatch(str,".") do    if d == "" then d=c else m = di2mono[d..c]        if m and m == "č" then d = "č" elseif d == "č" and not m then ret = ret.."č" d = c        elseif m then ret = ret.. m         d = "" else ret = ret.. d         d = c        end end end ret = ret.. d  return ret end

local function convert_back -- using regex only is too expensive -- doing it manually

ret ="" d = "" for c in mw.ustring.gmatch(form_,".") do     if c ==":" or c=="=" or c=="#" then elseif ret == "" and c == "ʼ" then elseif d == "s" and c == "h" then ret = ret.."x" elseif d == "s" and c == "s" then elseif d == "sh" and c == "š" then elseif d == "z" and c == "z" then elseif d == "zh" and c == "ž" then elseif c == "ğ" or c == "f" then d = c     elseif d == "ğ" then if umatch(c, "[eęiįéę́íį́]") then d="y" elseif umatch(c,"[oǫóǫ́]") then d="w" else d="gh" end ret=ret.. d..c     elseif d == "f" then if umatch(c, "[oǫóǫ́]") then d="h" else d="hw" end ret=ret..d..c     else d = mono2di[c] or c        ret = ret .. d     end end form_=ret end

--################################# -- RULES

-- declared as local above function fut_adjustment_conj fut_="di" if asp_=="yii" then fut_="ği:".. fut_ asp_="∅" end end

local function fut_adjustments -- di- positioning replace2("di:([yh][ií])", "%1:di")

-- high-tone future replace2("d[ií]:n[ií]", "dí:ní")

-- methateses that need early ordering replace ("ji:y(I)", "y%1:ji") replace2("(.[:#])ji:ği", "%1ği:ji") replace2("(.+)#yi:ği", "%1#yi") end

local function ni_adjustments if neuter_ then

if neuter_ == "∅" then replace2(":?ni=", "=") end

replace2("(.[#:])łi=", "%1l=")

else -- "active" verb -- yini/yíní tentatively managed hee replace2("yí:ni=", "ó=") -- yíníshta' replace2("ğí:ni=", "ó=") -- 'ííníshta' -- yinishyé-wolyé-joolyé-daolyé mess replace2("da#ği:ni=", "da#ğo=") replace2("^#ği:ni=", "#ğo=") replace2("ği:ni=", "oo=") -- conj-dominant if conj_ ~= "" then replace2(":ni=", ":ğe=") else -- ∅- or da- dominant replace2("^#ni=", "#yí=") replace2("^da#ni=", "da#yí=")

-- disj-dominant replace2("#ni=", "#=") end end end

local function yii_adjustments -- yi_doubling replace2("^#yi:ii", "#yi:yi:ii")

-- dis: ii > i, but da#ii > dayii or dei if mode_ == "impf" then replace2("da#ii=", "da#yi:ii=") else replace2("da#ii=", "da#yi=") end replace("(V)#ii(:?š?)=", "%1#i%2=")

-- ho adjustments: hwi+ii > hoo replace2("fi:ii(:?š?)=", "ho:o%1=")

-- object-dominant 2sg if obj ~= "" and conj_ == "" and rank == 2 then replace2("ini=", "iini=") end end

local function yi_adjustments

-- yi_doubling replace2("^#yi:ğí=", "#yi:yi:ğí=")

-- hwi-delab needed before gamma_del -- for 1sg: hwi:ğí= > hóó, not hwíí replace2("fi:ğí=", "ho:ğí=")

end

local function yi_d_adjustments

-- conjunct if rank == 3 then replace2("#(.+)ği=", "#%1ğo=") elseif rank == 1 then replace2("#(.+)ği:š=", "#%1ğe:š=") end

-- a-away + perfective = ee  replace2("ʼi#ği(:?š?)=", "ʼi#ğe%1=")

end

local function si_adjustments -- yini replace2("da#ği:([sz])=","da#ğo:%1=") replace2("ği:([sz])=","oo:%1=")

-- yiz + ł = yis replace2("z=ł:", "s=ł:")

-- #s=gan > sigan replace2("^#[sz]=([∅ł])", "#si=%1")

-- ni:soo > noo or sinoo if rank==5 then replace2("^#ni:soo", "#si:noo") replace2("^da#ni:soo", "da#si:noo") replace2("ni:soo", "noo") end end

local function seriative_h_deletion  --13 replace("(#.+:)hi(C0)","%1ii%2") replace2("(#.+:)hi:","%1yi:") end

local function d_effect -- handle 1dl cases first replace2( ":d=ł:l",  "=dl") replace2( ":d=[łl]:", "=l:") replace2( ":d=[d∅]:", "=d:")

replace2( "=∅:",     "=") -- apply d-effect -- special cases if stem_ == "zǫ́ǫ́z" or stem_=="zǫ́ǫ́s" then replace2("=d:z", "=d") elseif stem_ == "ʼeeł" or stem_ == "ʼéél" or stem_ == "ʼoł" then replace2("=d:", "=") else c = match("=d:(C)") if c then dc = d_effect_ or d_effect_map[c] or c      replace2("=d:"..c, "=".. dc) end end end

local function continuant_devoicing  --18 if nodevoice_ then return end

exp = surds.."([:=])"..voiced s, sep, v = match2(exp) if s and sep and v then f = devoicing[v] replace2(s..sep..v, s..sep..f)  end end

local function barred_l_deletion     --23 replace2("=ł:([sšł])", "=%1") end

local function h_deletion            --32 replace2(":h=ł:", "=ł") end

local function classifier_deletion   --30 replace2("([sš])=ł:", "%1=") end

local function cons_degemination     --26 -- replace(":?s=s", "=s") -- replace(":?š=š", "=š") end

local function pepet_vowel_insertion --46 replace("#(C0)", "#i%1") end

local function vowel_deletion        --12 replace("(C)i:(V)", "%1%2") end

local function tone_assimilation     --39 -- acceleration if not has_high then return end

--disjunct if not nospread_ and has_disj then h,l,c0 =match("(H#C?:?C?:?C?)(L)(C0)") if h and l and c0 then a = high_tone[l] replace2(h..l..c0, h..a..c0) end end

--conjunct t,h,l = match("#(.*)(H:?C?:C)(L)") if t and h and l then a = high_tone[l] replace2(t..h..l,t.. h..a)  end end

local function tone_lowering         --48 --used for  ní:ii --> nii --what about ní:oh --> nó ?

--[[ moved to paradigm adjustments

h, l = match("(H)(:L)") if h and l then replace2(h..l, low_tone[h]..l)  end ]] end

local function gamma_tone_assim -- acceleration if not has_high or nospread_ or not has_disj then return end

h,l,c0 = match("(H#ğ)(L)(C0)") if h and l and c0 then a = high_tone[l] replace2(h..l..c0, h..a..c0) end end

local function gamma_deletion        --41 replace2("(#.*)ğ([ieoíéó].*=)", "%1%2")

-- future mode might need a second run replace2("(#.*)ğ([ieoíéó].*=)", "%1%2") end

local function n_deletion             --? -- made it a late rule instead -- bą́:ná#ʼ:dí:š doesn't work, why?

-- chʼínéísííd doesn't contract, -- chʼééínísííd does.

-- acceleration if not (has_disj and has_high) then return end

c, v, c0, c1 = match(   "(C)(V):n[áé][#:][ií]?:?(C.-)V(.*=)") if c and v and c0 and c1 and c ~= "ʼ" then h = high_tone[v] or v      if h == "í" then h="é" end replace2(c..v..":n[áé]", c..h..h) end end

local function tone_raising          --49 -- acceleration if not has_high then return end

l,s,h = match("(L)([#:])([íá])") if l and s and h then a = high_tone[l] replace2(l..s..h, a..s..h)  end end

local function vowel_assimilation    --57

-- special cases if has_disj then c, c0 = match("(C)i#i(C0)") if c and c0 and c ~="n" and c ~= "ʼ" then replace2("i#i", "e#e") end end

-- a+o = aa dialects, 2dl/pl only if pref == "oh"  then replace2("([aá])#o", "%1#a") end

-- progressive v, s, i, c0 = match("(V)([#:])(I+)(C0)") if v and s and i and c0 and not ufind(v..s..i, "[aá]#ii") then a = assimilate(i, v)       replace2(v..s..i..c0,                v..s..a..c0) end -- regressive disj if has_disj then b, i, v, c0 = match(             "([^kqhtc])(I)#(V+)(C0)") if b and i and v and c0 then a = assimilate(i, v)    replace2(b..i.."#"..v..c0,             b..a.."#"..v..c0) end end

-- regressive conj -- Note: hwi:o > hwo:o, should > ho:o --       to manage in convert_back --       along with gho > wo

c,i,v = match("#(.*)(I):(V)") if c and i and v then a = assimilate(i,v) replace("#".. c.. i..":".. v,             "#".. c.. a..":".. v)  end end

local function vowel_absorption      --62 -- acceleration if not has_disj then return end -- acceleration if not has_high then return end

-- in C0 environment only if match("V#VC0") then replace2("([ʼł])í#i","%1í#") replace2("í#i","é#") replace2("ó#o","ó#") replace2("á#a","á#") replace2("é#e","é#") end

--[[ not necessarily faster...

c, h, l, c0 = match("(C)(H)#(L)(C0)") if c and h and l and c0 then if h=="í" and l=="i" or         h=="á" and l=="a" or         h=="ó" and l=="o" or         h=="é" and l=="e" then v=h if h == "í" and not(c=="ʼ" or c=="ł") then v="é" end

replace2(h.."#"..l,v.."#") end end ]] end

--function optative_tone_lowering --51

local function y_deletion            --42 -- acceleration if not has_disj then return end

-- short i only replace("(V)#y(I)([:=]C)", "%1#%2%3") end

local function vowel_fronting        --67 -- acceleration if not has_disj then return end

replace("([^kqht])a#(I)", "%1e#%2") replace("([^kqht])á#(I)", "%1é#%2") end

local function vowel_degemination    --47 -- TODO: might not be enough --      need refactoring replace("(I)[#:]ii", "%1i") replace("(O)[#:]oo", "%1o") end

local function seriative_assimilation -- split from vcv_assimilation -- must take place before ni-abs -- to prevent náhí- from assimilating

-- acceleration if not has_disj then return end

v, i, c0 = match("(V)#h(I)(C0)") if v and i and c0 then replace2(v.."#h"..i, v.."#h"..v)  end end

local function vcv_assimilation      --44 -- NOTE: seriative case has been split --       only at #-boundary? --       only in zero environment?

-- acceleration if not has_disj then return end

-- special case replace2("ʼi#ʼi([:=])", "ʼe#ʼe%1") -- regressive k,i,c,v = match("(C)(I)#([mkghʼ])(V)") if k and i and c and v and k~="y" then a = assimilate(i, v)    replace2(i.."#"..c..v,             a.."#"..c..v)  end end

local function i_to_a                --71 --NOTE: easier to split rule in 2 parts replace("ʼi=", "ʼa=") -- can't do that here -- because of ná'í- 2sg vs 3sg, unless -- i_to_a applies after tone_assim? --replace("ʼí=", "ʼá=") i,s,c= match("ʼ(I)([#:]=?)(C)") if i and s and c and c ~= "ʼ" then a = assimilate(i,"a") replace2( "ʼ"..i..s..c,               "ʼ"..a..s..c)   end end

local function delabialization1        --78 --TODO: C should exclude "y" here.

--NOTE: Getting huge contradictions --     with regards to the ordering of  --      the ho-to-ha, v-deletion, --     gamma-deletion, ni-absorption --     and pepet vowel insertion rules -- ==> handle some cases directly here -- ==> split hwi from kwí --     kwí must be done before pepet --     hwi must be done after gamma_del -- --  Maybe should replace all this with a  --  with a late labialization rule -- instead, where : --    ho + ííní  =  hwííní --    ho + eesh  =  hweesh...   -- but ho + í_C[  =  hóó by assimilation

-- acceleration if not has_high then return end

replace("qí([#:=]*C)", "kó%1") replace("qí#(O)", "kó#")

end

local function delabialization2 replace("fi(C0)", "ha%1") replace("fi:(C)", "ho:%1")

-- below managed as in convert_back --replace("f(O)", "h%1")

end

local function ni_absorption          --11 -- v = match2("([io]):ni=") if v then a = high_tone[v] replace2(v..":ni=", a.."=" ) end end

local function pg_strident_assimilation --83 -- might be other cases? replace2("ji:ʒi", "ji:ji")

-- might combine directly w/ j_el -- ji:dzi > ji:ji > i:ji end

local function j_deletion             --94 replace2("ji:ji", "i:ji") end

local function vowel_elision          --86 -- K = C - {y, ʼ}

-- "ji" doesn't elide in front of  -- another j (ex. dajijooba) if match2("ji:j") then return end

replace("(.[:#]ʼ)i(:KV)", "%1%2") replace("(.[:#][jʒ])i(:KV)", "%1%2") end

local function deaffrication          --89 replace("j:(C)", "ž:%1") replace("ʒ:(C)", "z:%1") end

local function glottal_zh_metathesis  --91 replace2("ʼ:ž:", "ž:ʼ:") end

local function glottal_cv_metathesis -- "far" hop allows glottal stop to  -- to hop "inside" 2sg prefix -- ex: bighá#ʼdííní > bighá#dííʼní -- but   bą́ą́#ʼdííní >   bą́ą́#ʼdííní

if hop_ == "far" then replace("(ž?:?ʼ):(d:?V+:?V?):?(K:?V+)", "%2:%1:%3") else replace("(ž?:?ʼ):(d:?V+):?(K:?V+)", "%2:%1:%3") end end

local function zh_cv_metathesis -- doesn't apply to jidinii, -- is that a more generic rule? if match2("ji:di:n:ii") then return end

replace("ji:(C:?V+):(C:?V+)", "%1:ž:%2") end

local function strident_assimilation  --81 if has_hiss then replace2("š", "s") elseif has_hush then replace2("s", "š") replace2("z", "ž") end end

local function syllabic_n             --100 -- split b/c of Lua regex limitations replace2("n[ia]([#:]ʼ?:?[dtjʒ])","n%1") replace2("n[íá]([#:]ʼ?:?[dtjʒ])","ń%1") replace("na#(ʼ?:?[sš]V)", "ni#%1") replace("ná#(ʼ?:?[sš]V)", "ní#%1")

replace2("na#(ž:ʼ?:?d)", "ni#%1") replace2("ná#(ž:ʼ?:?d)", "ní#%1") end

local function gamma_insertion        --6 -- acceleration if has_disj then return end

replace("^#(V)", "#ğ%1") end

local function gamma_gliding          --7 replace("ğ(I)", "y%1") replace("ğ(O)", "w%1") end

--################################# -- MAIN FUNCTION

function proc(person)

-- PERSON pers, numb = umatch(person,"([0-9])(..)") pers = tonumber(pers)

da  = (numb == "pl") and "da" or "" subj = (pers == 4)   and "ji" or "" obj = (obj_ == "yi"  and pers ~= 3 ) and "" or obj_

rank = (pers >= 3) and 3 or (numb == "sg") and pers or (pers + 3) if dashift_ and numb=="pl" and (dashift_ ~= "3" or rank==3) then asp_="si" end

-- Determination of paradigm to use asp2_ = asp_ if mode_ == "perf" then if asp_=="si" and umatch(conj_, "[dnh]i$") then asp2_ = asp_.."2" end

if cl_=="d" or cl_=="l" then asp2_ = asp2_ .. "d" end end

pref = paradigms[mode_][asp2_][rank] p, pf = split_pref(pref)

-- UNDERLYING FORM form_ = disj_ append(da); form_ = form_.."#" append(obj) append(subj) append(fut_) append(conj_) append(p) append(pf); form_ = form_.."=" append(cl_) append(stem_) --attempt at accelerating regex has_disj= (disj_..da ~= "") has_high= match("H")

if pers == 3 then replace2("P", "y") else replace2("P", "b") end

--form_=convert(form_) --if true then return form_ end -- IRREGULAR STEMS if stem_ == "ááh" then if person == "2sg" then replace("=∅:ááh", "=nááh") elseif pers == 2 then replace("=∅:ááh", "=∅:hááh") elseif rank == 3 then replace("=∅:ááh", "=∅:ğááh") end elseif stem_=="yą́" or stem_=="yį́į́ł" then if pers == 1 then replace("=∅:y", "=") elseif pers == 2 then replace("h=∅:y", "h=s") end end

-- MODE READJUSTMENT RULES

if mode_ == "fut" then fut_adjustments

elseif asp_ == "∅" then -- make this a more general rule? -- or amend the tone-lowering rule? -- or make "ní" its own paradigm?

-- how do manage both: -- á#ní:iid > ániildííl,   and --   ní:iid >  níilʼį́    ??

replace2("ní:ii", "ni:ii") replace2("ní:o:h","nó:h")

-- prevent ni-absorption if ni6 if rank == 3 then replace2("ni=", "ñi=") end

elseif asp_ == "ni" then ni_adjustments

elseif asp_ == "yii" then yii_adjustments

elseif asp2_ == "yi" then yi_adjustments

elseif asp2_ == "yid" then yi_d_adjustments

elseif asp_ == "si" then si_adjustments

end

-- PHONOLOGICAL RULES

seriative_h_deletion  --13

d_effect       -- 24,35,25

continuant_devoicing  --18 barred_l_deletion     --23 h_deletion            --32 classifier_deletion   --30

-- below ordering is tricky delabialization1       --78 --delab2 could go here w/ a labial rule --delabialization2

pepet_vowel_insertion --46 vowel_deletion        --12 --tone_assimilation     --39 --tone_lowering         --48 gamma_tone_assim      --hack gamma_deletion        --41

-- trying this here -- must be before ni-abs so that -- 2sg náhí- doesn't assimilate to náhá- seriative_assimilation

-- trying hwi-delab here -- must be before ni-abs -- hwi:ni-> ho:ni -> hó- delabialization2 ni_absorption         --11

-- moved tone_assim to later rule -- so as to allow proper hwi delab -- before tone assimilation kicks in -- ná#haso -> ná#háso and not ná#hwíso -- Might have to move it even after -- v-elision and deaffrication for -- words like łí#ji:di -> łí#zh:dí -- issue: but tone_assim must be before -- gamma_del for words like : -- ná#ghi > ná#ghí > ná#í -- ==> wrote a gamma_tone_assim rule --tone_assimilation

-- made it a late rule instead b/c -- y_deletion hasn't applied yet -- cf. chʼínéísííd (no contraction). -- Actually, chʼínéísííd doesn't  -- contract b/c éí is considered one -- vowel, so conditions are not met. -- chʼééínísííd on the other side does -- contract thanks to intervening ní. --n_deletion            --?

tone_raising          --49 vowel_assimilation    --57 vowel_absorption      --62 --optative_tone_lowering --51 y_deletion            --42 vowel_fronting        --67 vowel_degemination    --47

pg_strident_assimilation --83 j_deletion             --94 vowel_elision          --86 deaffrication          --89 glottal_zh_metathesis  --91

glottal_cv_metathesis zh_cv_metathesis strident_assimilation  --81

-- i_to_a must be before -- tone-assim but after ni-abs: --  2sg: ná#'i:ni > ná#'í --  3sg: ná#'i > ná#'a > ná#'á -- if tone_assim before, -- can't distinguish 2sg from 3sg --  3sg: ná#'i > ná#'í > ná#'á ??

i_to_a tone_assimilation vcv_assimilation       --44

-- LATE RULES

n_deletion --cons_degemination      --26 --i_to_a               --72 syllabic_n             --100

-- done in convert_back gamma_insertion        --III.6 --gamma_gliding          --III.7

-- FORMAT convert_back return form_ end

--##################################

local nv = require("Module:languages").getByCode("nv") local full_link = require("Module:links").full_link function link(term) return full_link{ term = term, lang = nv } end

function make_table(data)

mode = "IMPERFECTIVE" if mode_ == "perf" then mode = "PERFECTIVE" elseif mode_ == "fut" then mode = "FUTURE" end

return (string.gsub( [=[{| style="margin-bottom: .1em; margin-right: 1em; width: 50em; border: 1px solid #AAAAAA; border-collapse: collapse; text-align: center;" cellpadding="4" rules="all" ! style="width: 98px; background-color: #EFEAAA; text-align: left; font-size: 90%;" | ! style="width: 162px; background-color: #EFEFFF; font-size: 90%;" | singular ! style="width: 162px; background-color: #EFEFFF; font-size: 90%;" | duoplural ! style="width: 162px; background-color: #EFEFFF; font-size: 90%;" | plural ! style="background-color: #EFEFFF; text-align: left; font-size: 90%;" | 1st person ! style="background-color: #EFEFFF; text-align: left; font-size: 90%;" | 2nd person ! style="background-color: #EFEFFF; text-align: left; font-size: 90%;" | 3rd person ! style="background-color: #EFEFFF; text-align: left; font-size: 90%;" | 4th person '%{%{%{([^}]+)%}%}%}',	function(code) local form_data = data[code] return form_data and link(form_data) or code == 'mode' and mode or error('No content for the code ' .. code .. '.') end)) end
 * colspan="2" |
 * colspan="2" |
 * }]=],
 * }]=],

function make_string(data) return data.form_1sg.. " ".. data.form_1dl.. " "..data.form_1pl .. " "..data.form_2sg.. " ".. data.form_2dl.." "..data.form_2pl.. " ".. data.form_3sg.. " ".. data.form_3pl.. " "..data.form_4sg.. " "..data.form_4pl end

return p