The purpose of this module is to automatically generate conjugation tables for Navajo verbs, similar to the output of the {{nv-verbtable}} template.

Introduction

edit

This module is originally based on Kari's thesis (1978) with a sizeable number of additions, adaptations and fixes.

Overview of the supported features:

Covered Not covered
Mode - Imperfective (∅, ni)
- Perfective (yi, ni, si)
- Future (∅, yi)
- Iterative
- Optative
Persons - 1 to 4 - Unspecified
- Spatial
Numbers - singular
- dual
- plural
Paradigms - highly to fairly productive
- highly to fairly regular
- unproductive or rare
- too irregular
- seriative
Stem - must be provided

Usage

edit
  • function
Use conj when invoking the module directly, and show when invoking from a template.
|1=
Mode, to choose from: impf, perf or fut.
|2=
Aspect marker (position VII), to choose from , ni, si,yi or yii.
|3=
Classifier (position IX).
|4=
Stem (position X) must be provided by the user.
|disj=
Disjunct prefix (position 0/I).
If needed use : to separate multiple prefixes.
|obj=
Object prefix (position IV/V).
|conj=
Conjunct prefix (position VI).
If needed use : to separate multiple prefixes.

The input prefixes can usually be written in their usual orthography with the glottal stop, but mind the following special conventions:

  • a or ʼi
for either the a-away disjunct prefix or the unspecified object prefix.
  • á or ʼí
for the á-thus disjunct prefix
  • ho or hwi.
for the spatial object prefix
  • Pi
for the b/y indirect object alternation.

As some verbs exhibit a number of irregular alternations, additional options are provided for the user to help the module generate the correct form.

  • |nodevoice=1
Do not devoice stem initial consonant even after 1st person singular sh- or 2nd person duoplural h-.
  • |d-effect=
Provides the form taken by the stem initial consonant when following the 1st person duoplural iid- prefix, when different from the expected d-effect.
  • |nospread=1
Do not spread disjunct prefix high-tone to conjunct pre-stem syllable.
  • |hop=far
Perform glottal stop hopping further right inside a 2nd singular prefix.
  • |dashift=
=1
Shift to si- conjugation in the perfective plural for all persons.
=3
Shift to si- conjugation in the perfective plural for 3rd and 4th persons only.
  • |neuter=
=ni
Use ni- as the prefix for 3rd and 4th persons in ni- conjugations.
=∅
Use ∅- as the prefix for 3rd and 4th persons in ni- conjugations.


Finally, in case the module is unable to generate the correct output for a particular form using the general rules, it is possible for the user to provide it explicitly with the following optional arguments:

  • |1sg=
  • |2sg=
  • |3sg=
  • |4sg=
  • |1dl=
  • |2dl=
  • |1pl=
  • |2pl=
  • |3pl=
  • |4pl=

Alternative usage

edit

In the usage describe above, 4 parameters are mandatory (mode, aspect, classifier, stem) while the others are optional (including disjunct, object and conjunct prefixes).

There is also an alternative usage of the module with 7 mandatory parameters for mode, disjunct, obj, conjunct, aspect, classifier and stem, allowing a somewhat more condensed expression.

See examples below.

Examples

edit
{{#invoke:nv-conj|conj|impf|∅|∅|cha}}

or using the wrapper template {{nv-conj}} :

{{nv-conj|impf|∅|∅|cha}} 
IMPERFECTIVE singular duoplural plural
1st person yishcha yiicha deiicha
2nd person nicha wohcha daahcha
3rd person yicha daacha
4th person jicha dajicha


{{nv-conj|perf|disj=kʼi|obj=y|conj=di|yi|∅|lééł|d-effect=ly}} 

or using the condensed usage:

{{nv-conj|perf|kʼi|y|di|yi|∅|lééł|d-effect=ly}} 
PERFECTIVE singular duoplural plural
1st person kʼidíílééł kʼidiilyééł kʼidadiilyééł
2nd person kʼidíínílééł kʼidoolééł kʼidadoolééł
3rd person kʼiidíílééł kʼideidíílééł
4th person kʼizhdíílééł kʼidazhdíílééł


{{nv-conj|fut|disj=Pí|obj=ho|yii|ł|ʼááł}} 
FUTURE singular duoplural plural
1st person bíhwiideeshʼááł bíhwiidiilʼááł bídahwiidiilʼááł
2nd person bíhwiidííłʼááł bíhwiidoołʼááł bídahwiidoołʼááł
3rd person yíhwiidoołʼááł yídahwiidoołʼááł
4th person bíhwiizhdoołʼááł bídahwiizhdoołʼááł


Note that the following call would have given the same output:

{{nv-conj|fut|disj=Pí|obj=hwii|∅|ł|ʼááł}} 

As a matter of fact, it might be sometimes useful to try shuffling prefixes around until the correct forms be generated.

Unsupported paradigms

edit

Imperfective

edit
  • seriative
  • ...

Perfective

edit
  • "make"-verbs based on the -dzaa, -laa or -ʼį́į́d stems.
  • paradigms with ní ~ néé ~ née ~ nées alternations.
  • paradigms with díní ~ déé ~ dées alternations.
  • paradigms with di-si ~ diiz alternations.
  • most of yini-/yíní- paradigms.
  • seriative
  • ...

Future

edit
  • seriative
  • ...

--[[
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%;" | {{{mode}}}
! 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
| {{{form_1sg}}}
| {{{form_1dl}}}
| {{{form_1pl}}}
|-
! style="background-color: #EFEFFF; text-align: left; font-size: 90%;" | 2nd person
| {{{form_2sg}}}
| {{{form_2dl}}}
| {{{form_2pl}}}
|-
! style="background-color: #EFEFFF; text-align: left; font-size: 90%;" | 3rd person
| colspan="2" | {{{form_3sg}}}
| {{{form_3pl}}}
|-
! style="background-color: #EFEFFF; text-align: left; font-size: 90%;" | 4th person
| colspan="2" | {{{form_4sg}}}
| {{{form_4pl}}}
|}]=],
	'%{%{%{([^}]+)%}%}%}',
	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

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