User:Taokailam/Sandbox

From Wiktionary, the free dictionary
Jump to navigation Jump to search

--[=[

TODO:

   - Implement support for hollow roots in nif`al.
   - Implement support for geminate roots in pa`al and nif`al.
   - Implement smarter ktiv male spellings.
   - Implement support for mixed binyanim (such as for ניגש and יכול).
   - Implement support for alternative forms.

Reference:

   Input parameters:
       1: binyan abbreviation
       p: first root letter
       a: second root letter
       l: third root letter
   Root letters may be prefixed with ':' to include them literally, in which case they will not be
   parsed as hollow or weak and no dageshes will be added to or removed from them. Any of the
   final form codes or intermediate data below can also be given explicitly in the input.
   Binyan abbreviations:
       pa: pa`al
       pi: pi`el
       po: po`el/polel
       pu: pu`al
       poal: po`al/polal
       hif: hif`il
       huf: huf`al
       nif: nif`al
       hit: hitpa`el
       hitpo: hitpo`el/hitpolel
       hitpu: hitpu`al *rare modern coinage
   Tense abbreviations:
       inf: to-infinitive
       imp: imperative
       fut: future (a.k.a. imperfect or prefix conjugation)
       pres: present (a.k.a. active participle)
       past: past (a.k.a. perfect or suffix conjugation)
       noun: action noun
       pp: passive participle
   Gender abbreviations:
       s: singular *first person only
       p: plural *first person only
       ms: masculine singular
       fs: feminine singular
       mp: masculine plural
       fp: feminine plural
   Person abbreviations:
       1: first person
       2: second person
       3: third person
   Exhaustive list of final form codes:
       inf
       noun *optional
       pp *optional
       imp_ms
       imp_fs
       imp_mp
       imp_fp *obsolete
       fut_1s
       fut_2ms *always equal to fut_3fs
       fut_2fs
       fut_3ms
       fut_3fs *always equal to fut_2ms
       fut_1p
       fut_2mp
       fut_2fp *obsolete and always equal to fut_3fp
       fut_3mp
       fut_3fp *obsolete and always equal to fut_2fp
       past_1s
       past_2ms
       past_2fs
       past_3ms
       past_3fs
       past_1p
       past_2mp
       past_2fp
       past_3mp
       past_3fp *obsolete before Biblical Hebrew, therefore equal to past_3mp
       pres_ms
       pres_fs
       pres_mp
       pres_fp
   Intermediate data:
       root_p
       root_a
       root_l
       gem *whether to treat as geminate
       imp_pre_stem
       imp_prs_stem
       imp_mid_stem
       imp_fin_stem
       imp_suf_stem
       imp_ffp_stem
       fut_gem
       fut_char *characteristic vowel [a,i,u], pa`al only (default: u)
       fut_pre_stem
       fut_prs_stem
       fut_1sp_stem
       fut_mid_stem
       fut_fin_stem
       fut_suf_stem
       fut_ffp_stem
       pres_gem
       pres_short *if not empty, use the pa`el-present, pa`al only
       pres_stem
       past_gem
       past_char *characteristic vowel [a,i,u], pa`al only (default: a)
       past_pre_stem
       past_2p_pre_stem
       past_stem
       past_3_stem
       past_3ms_stem
       past_3fs_stem
       past_2p_stem

]=]

local m_utilities = require("Module:utilities") local m_links = require("Module:links") local m_strutils = require("Module:string utilities")

local export = {} local get_stems_for = {}

local lang = require("Module:languages").getByCode("he")

local convert_root_char = {

   ["א"] = "א",
   ["ב"] = "ב",
   ["בּ"] = "בּ", -- always hard
   ["בֿ"] = "בֿ", -- always soft
   ["ג"] = "ג",
   ["גּ"] = "גּ", -- always hard
   ["גֿ"] = "גֿ", -- always soft
   ["ד"] = "ד",
   ["דּ"] = "דּ", -- always hard
   ["דֿ"] = "דֿ", -- always soft
   ["ה"] = "ה",
   ["ו"] = "ו",
   ["ז"] = "ז",
   ["ח"] = "ח",
   ["ט"] = "ט",
   ["י"] = "י",
   ["כ"] = "כ",
   ["כּ"] = "כּ", -- always hard
   ["כֿ"] = "כֿ", -- always soft
   ["ל"] = "ל",
   ["מ"] = "מ",
   ["נ"] = "נ",
   ["ס"] = "ס",
   ["ע"] = "ע",
   ["פ"] = "פ",
   ["פּ"] = "פּ", -- always hard
   ["פֿ"] = "פֿ", -- always soft
   ["צ"] = "צ",
   ["ק"] = "ק",
   ["ר"] = "ר",
   ["ש"] = "שׁ", -- assume shin
   ["שׁ"] = "שׁ", -- shin
   ["שׂ"] = "שׂ", -- sin
   ["ת"] = "ת",
   ["תּ"] = "תּ", -- always hard
   ["תֿ"] = "תֿ", -- always soft

}

local convert_final_root_char = {

   ["ה"] = "י", -- make defective
   ["הּ"] = "ה", -- remove mappiq
   ["ו"] = "י", -- make defective
   ["ך"] = "כ", -- normalize final
   ["ךּ"] = "כּ", -- always hard, normalize final
   ["ךֿ"] = "כֿ", -- always soft, normalize final
   ["ם"] = "מ", -- normalize final
   ["ן"] = "נ", -- normalize final
   ["ף"] = "פ", -- normalize final
   ["ףּ"] = "פּ", -- always hard, normalize final
   ["ףֿ"] = "פֿ", -- always soft, normalize final
   ["ץ"] = "צ", -- normalize final

}

local forms_itp = {

   ["שׁ"] = "שְׁתּ",
   ["שׂ"] = "שְׂתּ",
   ["ס"] = "סְתּ",
   ["ז"] = "זְדּ",
   ["צ"] = "צְט",
   ["ת"] = {"ית", "תּ"},
   ["ד"] = {"יד", "דּ"},
   ["ט"] = {"יט", "טּ"},

}

local forms_etp = {

   ["ת"] = "תּ",
   ["ד"] = "דּ",
   ["ט"] = "טּ",

}

local forms = {

   ["בֿ"] = "ב",
   ["גֿ"] = "ג",
   ["דֿ"] = "ד",
   ["כֿ"] = "כ",
   ["פֿ"] = "פ",
   ["תֿ"] = "ת",

}

local forms_initial = {

   ["ב"] = "בּ",
   ["ג"] = "גּ",
   ["ד"] = "דּ",
   ["כ"] = "כּ",
   ["פ"] = "פּ",
   ["ת"] = "תּ",

}

local non_doubling_letters = {

   ["א"] = 1,
   ["ה"] = 2,
   ["ח"] = 3,
   ["ע"] = 2,
   ["ר"] = 1,

}

local forms_final_furtive = {

   ["ה"] = "הַּ",
   ["ח"] = "חַ",
   ["ע"] = "עַ",

}

local forms_final = {

   ["ה"] = "הּ",
   ["כ"] = "ךְ",
   ["כּ"] = "ךְּ",
   ["כֿ"] = "ךְ",
   ["מ"] = "ם",
   ["נ"] = "ן",
   ["פ"] = "ף",
   ["פֿ"] = "ף",
   ["צ"] = "ץ",

}

local short_gem_vowel = {

   ["a"] = "ַ",
   ["i"] = {"י", "ִ"},
   ["u"] = {"ו", "ֻ"},
   ["e"] = "ֶ",
   ["o"] = {"ו", "ָ"},

}

local lengthened_gem_vowel = {

   ["a"] = "ָ",
   ["i"] = {"י", "ֵ"},
   ["u"] = {"ו", "ֹ"},
   ["e"] = {"י", "ֵ"},
   ["o"] = {"ו", "ֹ"},

}

local mixed_gem_vowel = {

   ["a"] = "ַ",
   ["i"] = {"י", "ִ"},
   ["u"] = {"ו", "ֹ"},
   ["e"] = "ֶ",
   ["o"] = {"ו", "ֹ"},

}

local stressed_gem_vowel = {

   ["a"] = "ַ",
   ["i"] = "ֵ",
   ["u"] = {"ו", "ֹ"},
   ["e"] = "ֵ",
   ["o"] = {"ו", "ֹ"},

}

local doubling_type = {

   [1] = lengthened_gem_vowel,
   [2] = mixed_gem_vowel,
   [3] = short_gem_vowel,

}

local gutturals = {

   ["א"] = true,
   ["ה"] = true,
   ["ח"] = true,
   ["ע"] = true,

}

local char_vowel = {

   ["a"] = "ַ",
   ["i"] = "ֵ",
   ["u"] = {"ו", "ֹ"},
   ["A"] = "ָ",
   ["I"] = "ִי",
   ["U"] = "וּ",
   ["E"] = "ֵ",
   ["O"] = "וֹ",

}

local char_vowel_open = {

   ["a"] = "ָ",
   ["i"] = "ֵ",
   ["u"] = {"ו", "ֹ"},
   ["A"] = "ָ",
   ["I"] = "ִי",
   ["U"] = "וּ",
   ["E"] = "ֵ",
   ["O"] = "וֹ",

}

local char_vowel_short = {

   ["a"] = "ַ",
   ["i"] = "ֵ",
   ["u"] = {"ו", "ֹ"},
   ["A"] = "ַ",
   ["I"] = "ֵ",
   ["U"] = {"ו", "ֹ"},
   ["E"] = "ֵ",
   ["O"] = {"ו", "ֹ"},

}

local char_vowel_open_short = {

   ["a"] = "ָ",
   ["i"] = "ֵ",
   ["u"] = {"ו", "ֹ"},
   ["A"] = "ָ",
   ["I"] = "ֵ",
   ["U"] = {"ו", "ֹ"},
   ["E"] = "ֵ",
   ["O"] = {"ו", "ֹ"},

}

local char_vowel_closed = {

   ["a"] = "ַ",
   ["i"] = "ַ",
   ["u"] = {"ו", "ֹ"},
   ["A"] = "ַ",
   ["I"] = "ַ",
   ["U"] = {"ו", "ֹ"},
   ["E"] = "ַ",
   ["O"] = {"ו", "ֹ"},

}

local char_vowel_unstressed = {

   ["a"] = "ַ",
   ["i"] = "ַ",
   ["u"] = {"ו", "ָ"},
   ["A"] = "ַ",
   ["I"] = "ַ",
   ["U"] = {"ו", "ָ"},
   ["E"] = "ַ",
   ["O"] = {"ו", "ָ"},

}

local char_vowel_open_fp = {

   ["a"] = "ֶ",
   ["i"] = "ֶ",
   ["u"] = {"ו", "ֹ"},
   ["A"] = "ֶ",
   ["I"] = "ֶ",
   ["U"] = {"ו", "ֹ"},
   ["E"] = "ֶ",
   ["O"] = {"ו", "ֹ"},

}

local char_vowel_reduce = {

   ["a"] = nil,
   ["i"] = nil,
   ["u"] = nil,
   ["A"] = "ָ",
   ["I"] = "ִי",
   ["U"] = "וּ",
   ["E"] = "ֵ",
   ["O"] = "וֹ",

}

local chataf_vowel = {

   ["a"] = "ֲ",
   ["i"] = "ֱ",
   ["u"] = {"ו", "ֳ"},

}

local monophthongize = {

   ["י"] = "ֵי",
   ["ו"] = "וֹ",

}

local function gen_link(x)

   if type(x) == "table" then
       local pg = (lang:makeEntryName(x[1]))
       if pg == (lang:makeEntryName(x[2])) then
           return m_links.full_link({lang = lang, term = pg, alt = x[2]})
       else
           return m_links.full_link({lang = lang, term = pg, alt = pg .. " / " .. x[2]})
       end
   else
       if x == "-" then
           return "—" -- m-dash
       else
           return m_links.full_link({lang = lang, term = x})
       end
   end

end

local function process_args(args)

   for key, value in pairs(args) do
       local i = mw.ustring.find(value, "[\\/]")
       if i then
           args[key] = {mw.ustring.sub(value, 1, i - 1), mw.ustring.sub(value, i + 1)}
       end
   end

end

local function append_parts_2(a, b)

   if type(a) == "table" then
       if type(b) == "table" then
           return {a[1] .. b[1], a[2] .. b[2]}
       else
           return {a[1] .. (lang:makeEntryName(b)), a[2] .. b}
       end
   else
       if type(b) == "table" then
           return {(lang:makeEntryName(a)) .. b[1], a .. b[2]}
       else
           return a .. b
       end
   end

end

local function append_parts(a, ...)

   for _, b in ipairs({...}) do
       a = append_parts_2(a, b)
   end
   return a

end

local function equal(a, b)

   if type(a) == "table" then
       return type(b) == "table" and a[1] == b[1] and a[2] == b[2]
   else
       return a == b
   end

end

local function attach_t(x, novowel, assim_t)

   local is_table = (type(x) == "table")
   local wv = is_table and x[2] or x
   if mw.ustring.sub(wv, -2) == "תְ" then
       if is_table then
           return {x[1] .. "ת", mw.ustring.sub(wv, 1, -2) .. (novowel and "ְּ" or "ּ")}
       else
           return append_parts(mw.ustring.sub(wv, 1, -2), {"ת", (novowel and "ְּ" or "ּ")})
       end
   elseif assim_t and mw.ustring.sub(wv, -2) == "נְ" and (not is_table or mw.ustring.sub(x[1], -1) == "נ") then
       if is_table then
           return {mw.ustring.sub(x[1], 1, -2) .. "ת", mw.ustring.sub(wv, 1, -3) .. (novowel and "תְּ" or "תּ")}
       else
           return mw.ustring.sub(wv, 1, -3) .. (novowel and "תְּ" or "תּ")
       end
   else
       local dagesh = (mw.ustring.sub(wv, -1) == "ְ")
       return append_parts(x, dagesh and (novowel and "תְּ" or "תּ") or "ת")
   end

end

local function attach_n(x)

   local is_table = (type(x) == "table")
   local wv = is_table and x[2] or x
   if mw.ustring.sub(wv, -2) == "נְ" then
       if is_table then
           return {x[1], mw.ustring.sub(wv, 1, -2) .. "ּ"}
       else
           return mw.ustring.sub(wv, 1, -2) .. "ּ"
       end
   else
       return append_parts(x, "נ")
   end

end

local function get_form_initial(letter)

   if type(letter) == "table" then
       return letter
   elseif mw.ustring.sub(letter, 1, 1) == ":" then
       return mw.ustring.sub(letter, 2)
   else
       return forms_initial[letter] or forms[letter] or letter
   end

end

local function get_form_itp(letter)

   return forms_itp[letter] or append_parts("תְ", get_form_initial(letter))

end

local function get_form_etp(letter)

   return forms_etp[letter] or forms_itp[letter] or append_parts("תְ", get_form_initial(letter))

end

local function get_form_medial(letter)

   if type(letter) == "table" then
       return letter
   elseif mw.ustring.sub(letter, 1, 1) == ":" then
       return mw.ustring.sub(letter, 2)
   else
       return forms[letter] or letter
   end

end

local dagesh_or_rafe = {["ּ"] = true, ["ֿ"] = true} local function add_dagesh(letter)

   if type(letter) == "table" then
       return letter
   elseif mw.ustring.sub(letter, 1, 1) == ":" then
       return get_form_medial(letter)
   elseif non_doubling_letters[letter] or dagesh_or_rafe[mw.ustring.sub(letter, -1)] then
       return get_form_medial(letter)
   else -- TODO: Handle geresh
       return get_form_medial(letter) .. "ּ"
   end

end

local function get_form_double(letter, vowel, force_lengthening, stressed)

   local lengthen = non_doubling_letters[letter]
   if force_lengthening then lengthen = lengthen and 1 end
   if stressed then
       vowel = vowel and (lengthen and vowel == "a" and "ָ" or stressed_gem_vowel[vowel]) or ""
   else
       vowel = vowel and (lengthen and doubling_type[lengthen][vowel] or short_gem_vowel[vowel]) or ""
   end
   return append_parts(vowel, add_dagesh(letter))

end

local function get_form_final(letter, char)

   if type(letter) == "table" then
       return letter
   elseif mw.ustring.sub(letter, 1, 1) == ":" then
       return mw.ustring.sub(letter, 2)
   else
       return (char ~= "a" and char ~= "A" and forms_final_furtive[letter]) or forms_final[letter] or forms[letter] or letter
   end

end

local function get_elet_ending(letter)

   if letter == "א" then
       return "ֵאת"
   else
       local vowel = gutturals[letter] and "ַ" or "ֶ"
       return append_parts(vowel, get_form_medial(letter), vowel, "ת")
   end

end

local function ends_in_guttural(letter)

   if type(letter) == "table" then
       letter = letter[2]
   end
   return gutturals[mw.ustring.sub(letter, -1)]

end

local function shva_na(letter, vowel)

   return ends_in_guttural(letter) and chataf_vowel[vowel or "a"] or "ְ"

end

local function reduce(char, chataf)

   return char_vowel_reduce[char] or chataf and "ֲ" or "ְ"

end

local letters = "[א-ת]" local not_letters = "[^א-ת]" local modifiers = "[ּֿׁׂׄ׳]?" local separators = "[-־ %.,!|]?" local root_regex = "(" .. letters .. modifiers .. ")" .. separators

local function parse_root_part(part, last)

   if mw.ustring.sub(part, 1, 1) == ":" then
       return part
   end
   local letters = {}
   local len = 0
   local subber = function(letter)
       table.insert(letters, letter)
       len = len + 1
       return ""
   end
   local scraps = mw.ustring.gsub(part, root_regex, subber)
   if scraps ~= "" then
       return ":" .. part
   end
   if len < 1 then
       return nil
   elseif len == 1 then
       return part
   else
       local ret = ""
       for i, letter in ipairs(letters) do
           local letterx = last and i == len and convert_final_root_char[letter] or convert_root_char[letter]
           if not letterx then
               error("Unrecognized root letter '" .. letter .. "'.")
           end
           ret = append_parts(ret, get_form_medial(letterx), i == len and "" or "ְ")
       end
       if type(ret) == "table" then
           return ret
       else
           return ":" .. ret
       end
   end

end

local function parse_root(args)

   if args[2] == "" then args[2] = nil end
   if args[3] == "" then args[3] = nil end
   if args[4] == "" then args[4] = nil end
   if args[3] then
       args["p"] = args["p"] or parse_root_part(args[2])
       args["a"] = args["a"] or parse_root_part(args[3])
       args["l"] = args["l"] or parse_root_part(args[4], true)
   elseif args[2] then
       local radicals = {}
       local len = 0
       local subber = function(radical)
           table.insert(radicals, radical)
           len = len + 1
           return ""
       end
       local scraps = mw.ustring.gsub(args[2], root_regex, subber)
       if scraps ~= "" then
           error("Unrecognized characters in root.")
       end
       if len < 3 then
           error("Root must have at least three letters.")
       elseif len == 3 then
           args["p"] = args["p"] or radicals[1]
           args["a"] = args["a"] or radicals[2]
           args["l"] = args["l"] or radicals[3]
       elseif len == 4 then
           args["p"] = args["p"] or radicals[1]
           args["a"] = args["a"] or radicals[2]
           args["a2"] = args["a2"] or radicals[3]
           args["l"] = args["l"] or radicals[4]
       elseif len > 4 then
           error("Roots with more than four letters must be explicitly delimited.")
       end
   end

end

local function convert_root(args)

   for _, x in ipairs({"p", "a", "a2", "l"}) do
       if args[x] then
           local root_x = "root_" .. x
           if not args[root_x] then
               if mw.ustring.sub(args[x], 1, 1) == ":" then
                   args[root_x] = args[x]
               else
                   args[root_x] = x == "l" and convert_final_root_char[args[x]] or convert_root_char[args[x]]
                   if not args[root_x] then
                       error("Unrecognized root letter '" .. args[x] .. "'.")
                   end
               end
           end
       end
   end
   if args["root_a2"] then
       local a = append_parts(get_form_medial(args["root_a"]), "ְ", get_form_initial(args["root_a2"]))
       if type(a) ~= "table" then
           a = ":" .. a
       end
       args["root_a"] = a
       args["root_a2"] = nil
   end

end

local function conjugate(args)

   args["imp_pre_stem"] = args["imp_pre_stem"] or args["fut_pre_stem"]
   args["imp_mid_stem"] = args["imp_mid_stem"] or args["fut_mid_stem"]
   args["imp_fin_stem"] = args["imp_fin_stem"] or args["fut_fin_stem"]
   args["imp_suf_stem"] = args["imp_suf_stem"] or args["fut_suf_stem"]
   args["imp_prs_stem"] = args["imp_prs_stem"] or args["fut_prs_stem"]
   args["imp_ffp_stem"] = args["imp_ffp_stem"] or args["fut_ffp_stem"]
   local fp_long = args["fp_long"] and args["fp_long"] ~= "-"
   if fp_long then
       args["fut_long_stem"] = append_parts(args["fut_long_stem"] or args["fut_suf_stem"], "ֶי")
       args["imp_long_stem"] = append_parts(args["imp_long_stem"] or args["imp_suf_stem"], "ֶי")
   end
   if args["imp"] ~= "-" then
       if not args["imp_ms"] then
           args["imp_ms"] = append_parts(args["imp_pre_stem"], args["imp_mid_stem"], args["imp_fin_stem"])
       end
       if not args["imp_fs"] then
           args["imp_fs"] = append_parts(args["imp_prs_stem"] or args["imp_pre_stem"], args["imp_mid_stem"], args["imp_suf_stem"], "ִי")
       end
       if not args["imp_mp"] then
           args["imp_mp"] = append_parts(args["imp_prs_stem"] or args["imp_pre_stem"], args["imp_mid_stem"], args["imp_suf_stem"], "וּ")
       end
       if not args["imp_fp"] then
           args["imp_fp"] = append_parts(fp_long and (args["imp_long_pre_stem"] or args["imp_prs_stem"]) or args["imp_pre_stem"], args["imp_mid_stem"], attach_n(fp_long and args["imp_long_stem"] or args["imp_ffp_stem"]), "ָה")
       end
   end
   if args["fut"] ~= "-" then
       if not args["fut_1s"] then
           args["fut_1s"] = append_parts("א", args["fut_1sp_stem"] or args["fut_pre_stem"], args["fut_mid_stem"], args["fut_fin_stem"])
       end
       if not args["fut_2ms"] then
           args["fut_2ms"] = append_parts("תּ", args["fut_pre_stem"], args["fut_mid_stem"], args["fut_fin_stem"])
       end
       if not args["fut_2fs"] then
           args["fut_2fs"] = append_parts("תּ", args["fut_prs_stem"] or args["fut_pre_stem"], args["fut_mid_stem"], args["fut_suf_stem"], "ִי")
       end
       if not args["fut_3ms"] then
           args["fut_3ms"] = append_parts("י", args["fut_pre_stem"], args["fut_mid_stem"], args["fut_fin_stem"])
       end
       if not args["fut_3fs"] then
           args["fut_3fs"] = args["fut_2ms"]
       end
       if not args["fut_1p"] then
           args["fut_1p"] = append_parts("נ", args["fut_pre_stem"], args["fut_mid_stem"], args["fut_fin_stem"])
       end
       if not args["fut_2mp"] then
           args["fut_2mp"] = append_parts("תּ", args["fut_prs_stem"] or args["fut_pre_stem"], args["fut_mid_stem"], args["fut_suf_stem"], "וּ")
       end
       if not args["fut_2fp"] then
           args["fut_2fp"] = append_parts("תּ", fp_long and (args["fut_long_pre_stem"] or args["fut_prs_stem"]) or args["fut_pre_stem"], args["fut_mid_stem"], attach_n(fp_long and args["fut_long_stem"] or args["fut_ffp_stem"]), "ָה")
       end
       if not args["fut_3mp"] then
           args["fut_3mp"] = append_parts("י", args["fut_prs_stem"] or args["fut_pre_stem"], args["fut_mid_stem"], args["fut_suf_stem"], "וּ")
       end
       if not args["fut_3fp"] then
           args["fut_3fp"] = append_parts("תּ", fp_long and (args["fut_long_pre_stem"] or args["fut_prs_stem"]) or args["fut_pre_stem"], args["fut_mid_stem"], attach_n(fp_long and args["fut_long_stem"] or args["fut_ffp_stem"]), "ָה")
       end
   end
   if args["pres"] ~= "-" then
       args["pres_pre_stem"] = args["pres_pre_stem"] or ""
       args["pres_prs_stem"] = args["pres_prs_stem"] or args["pres_pre_stem"]
       if not args["pres_ms"] then
           args["pres_ms"] = append_parts(args["pres_pre_stem"], args["pres_ms_stem"])
       end
       if not args["pres_fs"] then
           args["pres_fs_stem"] = args["pres_fs_stem"] or append_parts(args["pres_suf_stem"], "ָה")
           args["pres_fs"] = append_parts(args["pres_prs_stem"], args["pres_fs_stem"])
       end
       if not args["pres_mp"] then
           args["pres_mp"] = append_parts(args["pres_prs_stem"], args["pres_suf_stem"], "ִים")
       end
       if not args["pres_fp"] then
           args["pres_fp"] = append_parts(args["pres_prs_stem"], args["pres_suf_stem"], "וֹת")
       end
   end
   if args["past"] ~= "-" then
       args["past_pre_stem"] = args["past_pre_stem"] or ""
       args["past_2p_pre_stem"] = args["past_2p_pre_stem"] or args["past_pre_stem"]
       local past_long = args["past_long"] and args["past_long"] ~= "-"
       if past_long then
           args["past_stem"] = append_parts(args["past_long_stem"] or args["past_3_stem"] or args["past_stem"], "וֹ")
           args["past_2p_stem"] = args["past_stem"]
           args["past_long_pre_stem"] = args["past_long_pre_stem"] or args["root_l"] == "י" and args["past_pre_stem"] or args["past_2p_pre_stem"]
       else
           args["past_2p_stem"] = args["past_2p_stem"] or args["past_stem"]
       end
       local assim_t = args["assim_t"] and args["assim_t"] ~= "-"
       if not args["past_1s"] then
           args["past_1s"] = append_parts(past_long and args["past_long_pre_stem"] or args["past_pre_stem"], attach_t(args["past_stem"], false, assim_t), "ִי")
       end
       if not args["past_2ms"] then
           args["past_2ms"] = append_parts(past_long and args["past_long_pre_stem"] or args["past_pre_stem"], attach_t(args["past_stem"], false, assim_t), "ָ")
       end
       if not args["past_2fs"] then
           args["past_2fs"] = append_parts(past_long and args["past_long_pre_stem"] or args["past_pre_stem"], attach_t(args["past_stem"], true, assim_t))
       end
       if not args["past_3ms"] then
           args["past_3ms"] = append_parts(args["past_pre_stem"], args["past_3ms_stem"])
       end
       if not args["past_3fs"] then
           args["past_3fs"] = append_parts(args["past_3fs_pre_stem"] or args["past_3_pre_stem"] or args["past_pre_stem"], args["past_3fs_stem"] or args["past_3_stem"], "ָה")
       end
       if not args["past_1p"] then
           args["past_1p"] = append_parts(past_long and args["past_long_pre_stem"] or args["past_pre_stem"], attach_n(args["past_stem"]), "וּ")
       end
       if not args["past_2mp"] then
           args["past_2mp"] = append_parts(past_long and args["past_long_pre_stem"] or args["past_2p_pre_stem"], attach_t(args["past_2p_stem"], false, assim_t), "ֶם")
       end
       if not args["past_2fp"] then
           args["past_2fp"] = append_parts(past_long and args["past_long_pre_stem"] or args["past_2p_pre_stem"], attach_t(args["past_2p_stem"], false, assim_t), "ֶן")
       end
       if not args["past_2mp2"] then
           args["past_2mp2"] = append_parts(past_long and args["past_long_pre_stem"] or args["past_pre_stem"], attach_t(args["past_stem"], false, assim_t), "ֶם")
       end
       if not args["past_2fp2"] then
           args["past_2fp2"] = append_parts(past_long and args["past_long_pre_stem"] or args["past_pre_stem"], attach_t(args["past_stem"], false, assim_t), "ֶן")
       end
       if not args["past_3mp"] then
           args["past_3mp"] = append_parts(args["past_3_pre_stem"] or args["past_pre_stem"], args["past_3_stem"], "וּ")
       end
       if not args["past_3fp"] then
           args["past_3fp"] = args["past_3mp"]
       end
   end
   args["inf"] = args["inf"] or "-"
   args["heading"] = "Conjugation of " .. gen_link(args["past_3ms"]) .. " (see also Appendix:Hebrew verbs)"

end

local function get_imp_stem_endings(args, root_l, char, chataf, keep_long_vowel, guttural_force_patach)

   if root_l == "א" then
       args["imp_fin_stem"] = args["imp_fin_stem"] or append_parts(keep_long_vowel and char_vowel_open[char] or char_vowel_open_short[char], "א")
       args["imp_suf_stem"] = args["imp_suf_stem"] or append_parts(reduce(char, chataf), "א")
       args["imp_ffp_stem"] = args["imp_ffp_stem"] or append_parts(char_vowel_open_fp[char], "א")
       args["imp_long_stem"] = args["imp_long_stem"] or append_parts(char == "a" and char_vowel_open[char] or reduce(char, chataf), "א")
   elseif root_l == "י" then
       args["imp_fin_stem"] = args["imp_fin_stem"] or "ֵה"
       args["imp_suf_stem"] = args["imp_suf_stem"] or ""
       args["imp_ffp_stem"] = args["imp_ffp_stem"] or "ֶי"
   else
       args["imp_fin_stem"] = args["imp_fin_stem"] or append_parts(keep_long_vowel and char_vowel[char] or guttural_force_patach and gutturals[root_l] and "ַ" or char_vowel_short[char], get_form_final(root_l, guttural_force_patach and (not keep_long_vowel) and gutturals[root_l] and "a" or char))
       args["imp_suf_stem"] = args["imp_suf_stem"] or append_parts(reduce(char, chataf), get_form_medial(root_l))
       args["imp_ffp_stem"] = args["imp_ffp_stem"] or append_parts(gutturals[root_l] and "ַ" or char_vowel_short[char], get_form_medial(root_l), "ְ")
       args["imp_long_stem"] = args["imp_long_stem"] or append_parts(char == "a" and char_vowel_open[char] or reduce(char, chataf), get_form_medial(root_l))
   end

end

local function get_fut_stem_endings(args, root_l, char, chataf)

   if root_l == "א" then
       args["fut_fin_stem"] = args["fut_fin_stem"] or append_parts(char_vowel_open[char], "א")
       args["fut_suf_stem"] = args["fut_suf_stem"] or append_parts(reduce(char, chataf), "א")
       args["fut_ffp_stem"] = args["fut_ffp_stem"] or append_parts(char_vowel_open_fp[char], "א")
       args["fut_long_stem"] = args["fut_long_stem"] or append_parts(char == "a" and char_vowel_open[char] or reduce(char, chataf), "א")
   elseif root_l == "י" then
       args["fut_fin_stem"] = args["fut_fin_stem"] or "ֶה"
       args["fut_suf_stem"] = args["fut_suf_stem"] or ""
       args["fut_ffp_stem"] = args["fut_ffp_stem"] or "ֶי"
   else
       args["fut_fin_stem"] = args["fut_fin_stem"] or append_parts(char_vowel[char], get_form_final(root_l, char))
       args["fut_suf_stem"] = args["fut_suf_stem"] or append_parts(reduce(char, chataf), get_form_medial(root_l))
       args["fut_ffp_stem"] = args["fut_ffp_stem"] or append_parts(gutturals[root_l] and "ַ" or char_vowel_short[char], get_form_medial(root_l), "ְ")
       args["fut_long_stem"] = args["fut_long_stem"] or append_parts(char == "a" and char_vowel_open[char] or reduce(char, chataf), get_form_medial(root_l))
   end

end

local function get_pres_stem_endings(args, root_l, char, chataf, use_elet, use_et)

   if root_l == "י" then
       args["pres_ms_stem"] = args["pres_ms_stem"] or "ֶה"
       args["pres_suf_stem"] = args["pres_suf_stem"] or ""
       if use_et then
           args["pres_fs_stem"] = args["pres_fs_stem"] or "ֵית"
       end
   else
       args["pres_ms_stem"] = args["pres_ms_stem"] or append_parts(char_vowel_open[char], get_form_final(root_l, char))
       args["pres_suf_stem"] = args["pres_suf_stem"] or append_parts(char == "a" and char_vowel_open[char] or reduce(char, chataf), get_form_medial(root_l))
       if use_elet then
           args["pres_fs_stem"] = args["pres_fs_stem"] or get_elet_ending(root_l)
       end
   end

end

local function get_past_stem_endings(args, root_l, char, chataf, kca, use_e)

   if root_l == "א" then
       args["past_stem"] = args["past_stem"] or append_parts(kca and char_vowel_open[char] or "ֵ", "א")
       args["past_3ms_stem"] = args["past_3ms_stem"] or append_parts(char_vowel_open[char], "א")
       args["past_3_stem"] = args["past_3_stem"] or append_parts(reduce(char, chataf), "א")
       args["past_long_stem"] = args["past_long_stem"] or append_parts(char == "a" and char_vowel_open[char] or reduce(char, chataf), "א")
   elseif root_l == "י" then
       args["past_stem"] = args["past_stem"] or use_e and "ֵי" or "ִי"
       args["past_3ms_stem"] = args["past_3ms_stem"] or "ָה"
       args["past_3fs_stem"] = args["past_3fs_stem"] or append_parts(reduce("a", chataf), "ת")
       args["past_3_stem"] = args["past_3_stem"] or ""
   else
       args["past_stem"] = args["past_stem"] or append_parts(char_vowel_closed[char], get_form_medial(root_l), "ְ")
       args["past_2p_stem"] = args["past_2p_stem"] or append_parts(char_vowel_unstressed[char], get_form_medial(root_l), "ְ")
       args["past_3ms_stem"] = args["past_3ms_stem"] or append_parts(char_vowel[char], get_form_final(root_l, char))
       args["past_3_stem"] = args["past_3_stem"] or append_parts(reduce(char, chataf), get_form_medial(root_l))
       args["past_long_stem"] = args["past_long_stem"] or append_parts(char == "a" and char_vowel_open[char] or reduce(char, chataf), get_form_medial(root_l))
   end

end

local function get_imp_stem_endings_gem(args, root_l, char)

   if root_l == "א" then
       args["imp_fin_stem"] = args["imp_fin_stem"] or append_parts(char == "a" and "ָ" or stressed_gem_vowel[char], "א")
       args["imp_suf_stem"] = args["imp_suf_stem"] or append_parts(char == "a" and "ָ" or stressed_gem_vowel[char], "א")
       args["imp_ffp_stem"] = args["imp_ffp_stem"] or append_parts(char ~= "u" and char ~= "o" and "ֶ" or stressed_gem_vowel[char], "א")
   else
       args["imp_fin_stem"] = args["imp_fin_stem"] or append_parts(stressed_gem_vowel[char], get_form_final(root_l, char))
       args["imp_suf_stem"] = args["imp_suf_stem"] or get_form_double(root_l, char, true, true)
       args["imp_ffp_stem"] = args["imp_ffp_stem"] or append_parts(gutturals[root_l] and "ַ" or stressed_gem_vowel[char], get_form_medial(root_l), "ְ")
       args["imp_long_stem"] = args["imp_long_stem"] or get_form_double(root_l, char, true)
   end

end

local function get_fut_stem_endings_gem(args, root_l, char)

   if root_l == "א" then
       args["fut_fin_stem"] = args["fut_fin_stem"] or append_parts(char == "a" and "ָ" or stressed_gem_vowel[char], "א")
       args["fut_suf_stem"] = args["fut_suf_stem"] or get_form_double("א", char)
       args["fut_ffp_stem"] = args["fut_ffp_stem"] or append_parts(char ~= "u" and char ~= "o" and "ֶ" or stressed_gem_vowel[char], "א")
   else
       args["fut_fin_stem"] = args["fut_fin_stem"] or append_parts(stressed_gem_vowel[char], get_form_final(root_l, char))
       args["fut_suf_stem"] = args["fut_suf_stem"] or get_form_double(root_l, char, true, true)
       args["fut_ffp_stem"] = args["fut_ffp_stem"] or append_parts(gutturals[root_l] and "ַ" or stressed_gem_vowel[char], get_form_medial(root_l), "ְ")
       args["fut_long_stem"] = args["fut_long_stem"] or get_form_double(root_l, char, true)
   end

end

local function get_pres_stem_endings_gem(args, root_l, char, short_a)

   if root_l == "א" then
       args["pres_ms_stem"] = args["pres_ms_stem"] or append_parts(char == "a" and "ָ" or stressed_gem_vowel[char], "א")
       args["pres_suf_stem"] = args["pres_suf_stem"] or get_form_double("א", char)
   else
       args["pres_ms_stem"] = args["pres_ms_stem"] or append_parts(char == "a" and (not short_a) and "ָ" or stressed_gem_vowel[char], get_form_final(root_l, char))
       args["pres_suf_stem"] = args["pres_suf_stem"] or get_form_double(root_l, char, true)
   end

end

local function get_past_stem_endings_gem(args, root_l, char)

   if root_l == "א" then
       args["past_stem"] = args["past_stem"] or append_parts(char == "a" and "ָ" or stressed_gem_vowel[char], "א")
       args["past_3ms_stem"] = args["past_3ms_stem"] or append_parts(char == "a" and "ָ" or stressed_gem_vowel[char], "א")
       args["past_3_stem"] = args["past_3_stem"] or get_form_double("א", char)
   else
       args["past_stem"] = args["past_stem"] or append_parts((char == "u" or char == "o") and {"ו", "ֹ"} or "ַ", get_form_medial(root_l), "ְ")
       args["past_2p_stem"] = args["past_2p_stem"] or append_parts((char == "u" or char == "o") and {"ו", "ָ"} or "ַ", get_form_medial(root_l), "ְ")
       args["past_3ms_stem"] = args["past_3ms_stem"] or append_parts(stressed_gem_vowel[char], get_form_final(root_l, char))
       args["past_3_stem"] = args["past_3_stem"] or get_form_double(root_l, char, true, true)
       args["past_long_stem"] = args["past_long_stem"] or get_form_double(root_l, char, true)
   end

end

get_stems_for["pa"] = function(args, categories)

   local root_p = args["root_p"]
   local root_a = args["root_a"]
   local root_l = args["root_l"]
   if args["hollow"] ~= "-" then
       local past_char = args["past_char"] or "A"
       local fut_char = args["fut_char"] or (root_a == "י" and "I") or "U"
       local imp_char = args["imp_char"] or fut_char
       local inf_char = args["inf_char"] or fut_char
       local pres_char = args["pres_char"] or past_char
       local prefix_vowel = (fut_char == "O" and past_char == "O" and args["prefix_e"] ~= "-") and "ֵ" or "ָ"
       args["inf"] = args["inf"] or append_parts("ל", prefix_vowel, get_form_medial(root_p), char_vowel[inf_char], get_form_final(root_l, inf_char))
       args["noun"] = args["noun"] or append_parts(get_form_initial(root_p), "ִי", get_form_medial(root_l), "ָה")
       args["imp_pre_stem"] = args["imp_pre_stem"] or get_form_initial(root_p)
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts(prefix_vowel, get_form_medial(root_p))
       args["fut_long_pre_stem"] = args["fut_long_pre_stem"] or append_parts("ְ", get_form_medial(root_p))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_imp_stem_endings(args, root_l, imp_char, gutturals[root_p], true)
       get_fut_stem_endings(args, root_l, fut_char, gutturals[root_p])
       args["pres_pre_stem"] = args["pres_pre_stem"] or get_form_initial(root_p)
       get_pres_stem_endings(args, root_l, pres_char, gutturals[root_p], false, false)
       args["past_pre_stem"] = args["past_pre_stem"] or get_form_initial(root_p)
       get_past_stem_endings(args, root_l, past_char, gutturals[root_p], true, false)
   elseif false then
       -- reserved for special cases
   else
       local past_char = args["past_char"] or "a"
       local fut_char = args["fut_char"]
       local imp_char = args["imp_char"]
       local inf_char = args["inf_char"]
       local pres_char = args["pres_char"]
       local if_ = nil
       local ef_ = nil
       local suf_if_ = nil
       if args["assim"] ~= "-" then
           fut_char = fut_char or ((args["elide"] ~= "-" or gutturals[root_l]) and "a") or "u"
           if_ = get_form_double(root_a, "i", true)
           ef_ = get_form_double(root_a, "e", true)
       elseif args["elide"] ~= "-" then
           if root_p == "א" then
               fut_char = fut_char or "a"
               if_ = append_parts("ֹא", get_form_medial(root_a))
               ef_ = append_parts({"ו", "ֹ"}, get_form_medial(root_a))
           else
               fut_char = fut_char or (gutturals[root_l] and root_l ~= "א" and "a") or "i"
               if_ = append_parts("ֵ", get_form_medial(root_a))
           end
       elseif monophthongize[root_p] then
           fut_char = fut_char or "a"
           if_ = append_parts("ִי", get_form_medial(root_a))
       elseif gutturals[root_p] then
           fut_char = fut_char or ((gutturals[root_l] or gutturals[root_a]) and "a") or "u"
           local v1 = (root_p == "א" or (root_l == "י" and root_p ~= "ע") or (root_l ~= "י" and fut_char == "a")) and "ֶ" or "ַ"
           if args["nochataf"] then
               if root_a == "י" then v1 = "ִ" end
               if_ = append_parts(v1, get_form_medial(root_p), "ְ", get_form_initial(root_a))
               ef_ = append_parts("ֶ", get_form_medial(root_p), "ְ", get_form_initial(root_a))
           else
               local v2 = v1 == "ֶ" and "ֱ" or "ֲ"
               if_ = append_parts(v1, get_form_medial(root_p), v2, get_form_medial(root_a))
               ef_ = append_parts("ֶ", get_form_medial(root_p), "ֱ", get_form_medial(root_a))
               suf_if_ = append_parts(v1, get_form_medial(root_p), v1, get_form_medial(root_a))
           end
       else
           fut_char = fut_char or ((gutturals[root_l] or gutturals[root_a]) and "a") or "u"
           if_ = append_parts("ִ", get_form_medial(root_p), "ְ", get_form_initial(root_a))
           ef_ = append_parts("ֶ", get_form_medial(root_p), "ְ", get_form_initial(root_a))
       end
       local inf_if_ = nil
       local p_ = nil
       if args["elide"] ~= "-" and root_p ~= "א" then
           inf_char = inf_char or "i"
           imp_char = imp_char or fut_char
           args["inf"] = args["inf"] or append_parts("לָ", get_form_medial(root_a), get_elet_ending(root_l))
           p_ = get_form_initial(root_a)
       elseif gutturals[root_p] then
           inf_char = inf_char or "u"
           imp_char = imp_char or (root_p == "א" and args["elide"] ~= "-" and (((gutturals[root_l] or gutturals[root_a]) and "a") or "u")) or fut_char
           local v1 = (root_p == "א" or (root_l ~= "י" and inf_char == "a")) and "ֶ" or "ַ"
           if args["nochataf"] then
               if root_a == "י" then v1 = "ִ" end
               inf_if_ = append_parts(v1, get_form_medial(root_p), "ְ", get_form_initial(root_a))
           else
               local v2 = v1 == "ֶ" and "ֱ" or "ֲ"
               inf_if_ = append_parts(v1, get_form_medial(root_p), v2, get_form_medial(root_a))
           end
           p_ = append_parts(get_form_initial(root_p), root_p == "א" and "ֱ" or "ֲ", get_form_medial(root_a))
           if root_l ~= "י" and root_p == "א" and gutturals[root_a] then
               args["imp_prs_stem"] = args["imp_prs_stem"] or append_parts(get_form_initial(root_p), "ֶ", get_form_medial(root_a))
               args["imp_suf_stem"] = args["imp_suf_stem"] or append_parts("ֱ", get_form_medial(root_l))
           end
       else
           inf_char = inf_char or "u"
           imp_char = imp_char or fut_char
           inf_if_ = if_
           p_ = append_parts(get_form_initial(root_p), shva_na(root_p), get_form_medial(root_a))
       end
       args["inf"] = args["inf"] or append_parts("ל", inf_if_, root_l == "י" and "וֹת" or append_parts(char_vowel[inf_char], get_form_final(root_l, inf_char)))
       args["noun"] = args["noun"] or append_parts(get_form_initial(root_p), shva_na(root_p), get_form_medial(root_a), "ִי", root_l == "י" and "ּ" or get_form_medial(root_l), "ָה")
       args["pp"] = args["pp"] or append_parts(get_form_initial(root_p), "ָ", get_form_medial(root_a), "וּ", get_form_final(root_l, "U"))
       args["imp_pre_stem"] = args["imp_pre_stem"] or p_
       if root_l ~= "י" and (args["elide"] == "-" or root_p == "א") then
           if gutturals[root_a] then
               args["imp_prs_stem"] = args["imp_prs_stem"] or append_parts(get_form_initial(root_p), "ַ", get_form_medial(root_a))
               if imp_char ~= "u" and imp_char ~= "i" then
                   args["imp_long_pre_stem"] = args["imp_long_pre_stem"] or args["imp_pre_stem"]
               end
               args["imp_suf_stem"] = args["imp_suf_stem"] or append_parts("ֲ", get_form_medial(root_l))
           else
               args["imp_prs_stem"] = args["imp_prs_stem"] or append_parts(get_form_initial(root_p), "ִ", get_form_medial(root_a))
           end
       end
       args["fut_pre_stem"] = args["fut_pre_stem"] or if_
       args["fut_1sp_stem"] = args["fut_1sp_stem"] or ef_
       if root_l ~= "י" then
           args["fut_prs_stem"] = args["fut_prs_stem"] or suf_if_
       end
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_imp_stem_endings(args, root_l, imp_char, ends_in_guttural(root_a))
       get_fut_stem_endings(args, root_l, fut_char, ends_in_guttural(root_a))
       if args["pres_short"] and args["pres_short"] ~= "" then
           -- pa`el
           args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts(get_form_initial(root_p), "ָ", get_form_medial(root_a))
           if root_l ~= "י" then
               args["pres_prs_stem"] = args["pres_prs_stem"] or append_parts(get_form_initial(root_p), shva_na(root_p), get_form_medial(root_a))
           end
           get_pres_stem_endings(args, root_l, pres_char or "E", ends_in_guttural(root_a), false, false)
       else
           -- po`el
           args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts(get_form_initial(root_p), "וֹ", get_form_medial(root_a))
           get_pres_stem_endings(args, root_l, pres_char or "i", ends_in_guttural(root_a), true, false)
       end
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts(get_form_initial(root_p), "ָ", get_form_medial(root_a))
       args["past_2p_pre_stem"] = args["past_2p_pre_stem"] or append_parts(get_form_initial(root_p), shva_na(root_p), get_form_medial(root_a))
       if past_char == "i" or past_char == "u" then
           args["past_long_pre_stem"] = args["past_long_pre_stem"] or append_parts(get_form_initial(root_p), gutturals[root_a] and "ַ" or "ִ", get_form_medial(root_a))
       end
       get_past_stem_endings(args, root_l, past_char, ends_in_guttural(root_a), true, false)
   end

end

get_stems_for["pi"] = function(args, categories)

   local root_p = args["root_p"]
   local root_a = args["root_a"]
   local root_l = args["root_l"]
   local past_char = args["past_char"] or "i"
   local fut_char = args["fut_char"] or "i"
   local imp_char = args["imp_char"] or fut_char
   local inf_char = args["inf_char"] or fut_char
   local pres_char = args["pres_char"] or "i"
   if false then
       -- reserved for special cases
   elseif false then
       -- reserved for special cases
   else
       args["inf"] = args["inf"] or append_parts("לְ", get_form_medial(root_p), get_form_double(root_a, "a"), root_l == "י" and "וֹת" or append_parts(char_vowel[inf_char], get_form_final(root_l, inf_char)))
       args["noun"] = args["noun"] or append_parts(get_form_initial(root_p), get_form_double(root_a, "i"), "וּ", get_form_final(root_l, "U"))
       args["imp_pre_stem"] = args["imp_pre_stem"] or append_parts(get_form_initial(root_p), get_form_double(root_a, "a"))
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts("ְ", get_form_medial(root_p), get_form_double(root_a, "a"))
       args["fut_1sp_stem"] = args["fut_1sp_stem"] or append_parts("ֲ", get_form_medial(root_p), get_form_double(root_a, "a"))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_imp_stem_endings(args, root_l, imp_char, ends_in_guttural(root_a))
       get_fut_stem_endings(args, root_l, fut_char, ends_in_guttural(root_a))
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מְ", get_form_medial(root_p), get_form_double(root_a, "a"))
       get_pres_stem_endings(args, root_l, pres_char, ends_in_guttural(root_a), true, false)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts(get_form_initial(root_p), get_form_double(root_a, "i"))
       get_past_stem_endings(args, root_l, past_char, ends_in_guttural(root_a), false, false)
   end

end

get_stems_for["po"] = function(args, categories)

   local root_p = args["root_p"]
   local root_a = args["hollow"] == "-" and args["root_a"] or args["root_l"]
   local root_l = args["root_l"]
   local past_char = args["past_char"] or "i"
   local fut_char = args["fut_char"] or "i"
   local imp_char = args["imp_char"] or fut_char
   local inf_char = args["inf_char"] or fut_char
   local pres_char = args["pres_char"] or "i"
   if false then
       -- reserved for special cases
   elseif false then
       -- reserved for special cases
   else
       args["inf"] = args["inf"] or append_parts("לְ", get_form_medial(root_p), "וֹ", get_form_medial(root_a), root_l == "י" and "וֹת" or append_parts(char_vowel[inf_char], get_form_final(root_l, inf_char)))
       -- args["noun"] = args["noun"] or append_parts(get_form_initial(root_p), "וֹ", get_form_medial(root_a), shva_na(root_a), get_form_medial(root_l), "וּת")
       args["noun"] = args["noun"] or append_parts(get_form_initial(root_p), "ִי", get_form_medial(root_a), "וּ", get_form_final(root_l, "U"))
       args["imp_pre_stem"] = args["imp_pre_stem"] or append_parts(get_form_initial(root_p), "וֹ", get_form_medial(root_a))
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts("ְ", get_form_medial(root_p), "וֹ", get_form_medial(root_a))
       args["fut_1sp_stem"] = args["fut_1sp_stem"] or append_parts("ֲ", get_form_medial(root_p), "וֹ", get_form_medial(root_a))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_imp_stem_endings(args, root_l, imp_char, ends_in_guttural(root_a))
       get_fut_stem_endings(args, root_l, fut_char, ends_in_guttural(root_a))
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מְ", get_form_medial(root_p), "וֹ", get_form_medial(root_a))
       get_pres_stem_endings(args, root_l, pres_char, ends_in_guttural(root_a), true, false)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts(get_form_initial(root_p), "וֹ", get_form_medial(root_a))
       get_past_stem_endings(args, root_l, past_char, ends_in_guttural(root_a), false, false)
   end

end

get_stems_for["pu"] = function(args, categories)

   local root_p = args["root_p"]
   local root_a = args["root_a"]
   local root_l = args["root_l"]
   local past_char = args["past_char"] or "a"
   local fut_char = args["fut_char"] or "a"
   local pres_char = args["pres_char"] or "a"
   if false then
       -- reserved for special cases
   elseif false then
       -- reserved for special cases
   else
       args["imp"] = args["imp"] or "-"
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts("ְ", get_form_medial(root_p), get_form_double(root_a, "u"))
       args["fut_1sp_stem"] = args["fut_1sp_stem"] or append_parts("ֲ", get_form_medial(root_p), get_form_double(root_a, "u"))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_fut_stem_endings(args, root_l, fut_char, ends_in_guttural(root_a))
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מְ", get_form_medial(root_p), get_form_double(root_a, "u"))
       get_pres_stem_endings(args, root_l, pres_char, ends_in_guttural(root_a), true, false)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts(get_form_initial(root_p), get_form_double(root_a, "u"))
       get_past_stem_endings(args, root_l, past_char, ends_in_guttural(root_a), false, true)
   end

end

get_stems_for["poal"] = function(args, categories)

   local root_p = args["root_p"]
   local root_a = args["hollow"] == "-" and args["root_a"] or args["root_l"]
   local root_l = args["root_l"]
   local past_char = args["past_char"] or "a"
   local fut_char = args["fut_char"] or "a"
   local pres_char = args["pres_char"] or "a"
   if false then
       -- reserved for special cases
   elseif false then
       -- reserved for special cases
   else
       args["imp"] = args["imp"] or "-"
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts("ְ", get_form_medial(root_p), "וֹ", get_form_medial(root_a))
       args["fut_1sp_stem"] = args["fut_1sp_stem"] or append_parts("ֲ", get_form_medial(root_p), "וֹ", get_form_medial(root_a))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_fut_stem_endings(args, root_l, fut_char, ends_in_guttural(root_a))
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מְ", get_form_medial(root_p), "וֹ", get_form_medial(root_a))
       get_pres_stem_endings(args, root_l, pres_char, ends_in_guttural(root_a), true, false)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts(get_form_initial(root_p), "וֹ", get_form_medial(root_a))
       get_past_stem_endings(args, root_l, past_char, ends_in_guttural(root_a), false, true)
   end

end

get_stems_for["hif"] = function(args, categories)

   local root_p = args["root_p"]
   local root_a = args["root_a"]
   local root_l = args["root_l"]
   local past_char = args["past_char"] or args["geminate"] ~= "-" and "i" or "I"
   local fut_char = args["fut_char"] or args["geminate"] ~= "-" and "i" or "I"
   local imp_char = args["imp_char"] or fut_char
   local inf_char = args["inf_char"] or fut_char
   local pres_char = args["pres_char"] or args["geminate"] ~= "-" and "i" or "I"
   if args["hollow"] ~= "-" then
       args["inf"] = args["inf"] or append_parts("לְהָ", get_form_medial(root_p), char_vowel[inf_char], get_form_final(root_l, inf_char))
       args["noun"] = args["noun"] or append_parts(gutturals[root_p] and "הֶ" or "הֲ", get_form_medial(root_p), "ָ", get_form_medial(root_l), "ָה")
       args["imp_pre_stem"] = args["imp_pre_stem"] or append_parts("הָ", get_form_medial(root_p))
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts("ָ", get_form_medial(root_p))
       args["imp_long_pre_stem"] = args["imp_long_pre_stem"] or append_parts(gutturals[root_p] and "הַ" or "הֲ", get_form_medial(root_p))
       args["fut_long_pre_stem"] = args["fut_long_pre_stem"] or append_parts("ְ", get_form_medial(root_p))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_imp_stem_endings(args, root_l, imp_char, gutturals[root_p], false, true)
       get_fut_stem_endings(args, root_l, fut_char, gutturals[root_p])
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מֵ", get_form_medial(root_p))
       args["pres_prs_stem"] = args["pres_prs_stem"] or append_parts("מְ", get_form_medial(root_p))
       get_pres_stem_endings(args, root_l, pres_char, gutturals[root_p], false, false)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts("הֵ", get_form_medial(root_p))
       args["past_2p_pre_stem"] = args["past_2p_pre_stem"] or append_parts(gutturals[root_p] and "הַ" or "הֲ", get_form_medial(root_p))
       get_past_stem_endings(args, root_l, past_char, gutturals[root_p], false, true)
   elseif args["geminate"] ~= "-" then
       args["inf"] = args["inf"] or append_parts("לְהָ", get_form_medial(root_p), char_vowel[inf_char], get_form_final(root_l, inf_char))
       --- args["noun"] = args["noun"] or append_parts(gutturals[root_p] and (non_doubling_letters[root_l] and "הֶ" or "הַ") or "הֲ", get_form_medial(root_p), get_form_double(root_l, "a", true), "ָה")
       args["noun"] = args["noun"] or append_parts(gutturals[root_p] and "הֶ" or "הֲ", get_form_medial(root_p), "ָ", get_form_medial(root_l), "ָה")
       args["imp_pre_stem"] = args["imp_pre_stem"] or append_parts("הָ", get_form_medial(root_p))
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts("ָ", get_form_medial(root_p))
       args["imp_long_pre_stem"] = args["imp_long_pre_stem"] or append_parts(gutturals[root_p] and "הַ" or "הֲ", get_form_medial(root_p))
       args["fut_long_pre_stem"] = args["fut_long_pre_stem"] or append_parts("ְ", get_form_medial(root_p))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_imp_stem_endings_gem(args, root_l, imp_char)
       get_fut_stem_endings_gem(args, root_l, fut_char)
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מֵ", get_form_medial(root_p))
       args["pres_prs_stem"] = args["pres_prs_stem"] or append_parts("מְ", get_form_medial(root_p))
       get_pres_stem_endings_gem(args, root_l, pres_char)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts("הֵ", get_form_medial(root_p))
       args["past_2p_pre_stem"] = args["past_2p_pre_stem"] or append_parts(gutturals[root_p] and "הַ" or "הֲ", get_form_medial(root_p))
       get_past_stem_endings_gem(args, root_l, past_char)
   elseif false then
       -- reserved for special cases
   else
       local af_ = nil
       local if_ = nil
       if args["assim"] ~= "-" then
           af_ = append_parts(gutturals[root_a] and "ָ" or "ַ", add_dagesh(root_a))
           if_ = append_parts(gutturals[root_a] and "ֵ" or "ִ", add_dagesh(root_a))
       elseif monophthongize[root_p] then
           af_ = append_parts(monophthongize[root_p], get_form_medial(root_a))
           if_ = af_
       elseif gutturals[root_p] then
           if args["nochataf"] then
               af_ = append_parts("ַ", get_form_medial(root_p), "ְ", get_form_initial(root_a))
               if_ = append_parts("ֶ", get_form_medial(root_p), "ְ", get_form_initial(root_a))
           else
               af_ = append_parts("ַ", get_form_medial(root_p), "ֲ", get_form_medial(root_a))
               if_ = append_parts("ֶ", get_form_medial(root_p), "ֱ", get_form_medial(root_a))
           end
       else
           af_ = append_parts("ַ", get_form_medial(root_p), "ְ", get_form_initial(root_a))
           if_ = append_parts(root_a == "א" and root_l == "י" and "ֶ" or "ִ", get_form_medial(root_p), "ְ", get_form_initial(root_a))
       end
       args["inf"] = args["inf"] or append_parts("לְה", af_, root_l == "י" and "וֹת" or append_parts(char_vowel[inf_char], get_form_final(root_l, inf_char)))
       args["noun"] = args["noun"] or append_parts("ה", af_, "ָ", get_form_medial(root_l), "ָה")
       args["imp_pre_stem"] = args["imp_pre_stem"] or append_parts("ה", af_)
       args["fut_pre_stem"] = args["fut_pre_stem"] or af_
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_imp_stem_endings(args, root_l, imp_char, ends_in_guttural(root_a), false, args["assim"] ~= "-")
       get_fut_stem_endings(args, root_l, fut_char, ends_in_guttural(root_a))
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מ", af_)
       get_pres_stem_endings(args, root_l, pres_char, ends_in_guttural(root_a), false, false)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts("ה", if_)
       get_past_stem_endings(args, root_l, past_char, ends_in_guttural(root_a), false, true)
   end

end

get_stems_for["huf"] = function(args, categories)

   local root_p = args["root_p"]
   local root_a = args["root_a"]
   local root_l = args["root_l"]
   local past_char = args["past_char"] or "a"
   local fut_char = args["fut_char"] or "a"
   local pres_char = args["pres_char"] or "a"
   if args["hollow"] ~= "-" then
       args["imp"] = args["imp"] or "-"
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts("וּ", get_form_medial(root_p))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_fut_stem_endings(args, root_l, fut_char, gutturals[root_p])
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מוּ", get_form_medial(root_p))
       get_pres_stem_endings(args, root_l, pres_char, gutturals[root_p], true, true)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts("הוּ", get_form_medial(root_p))
       get_past_stem_endings(args, root_l, past_char, gutturals[root_p], false, true)
   elseif args["geminate"] ~= "-" then
       args["imp"] = args["imp"] or "-"
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts("וּ", get_form_medial(root_p))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_fut_stem_endings_gem(args, root_l, fut_char)
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מוּ", get_form_medial(root_p))
       get_pres_stem_endings_gem(args, root_l, pres_char)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts("הוּ", get_form_medial(root_p))
       get_past_stem_endings_gem(args, root_l, past_char)
   elseif false then
       -- reserved for special cases
   else
       local uf_ = nil
       if args["assim"] ~= "-" then
           uf_ = append_parts({"ו", "ֻ"}, add_dagesh(root_a))
       elseif monophthongize[root_p] then
           uf_ = append_parts("וּ", get_form_medial(root_a))
       elseif gutturals[root_p] then
           if args["nochataf"] then
               uf_ = append_parts({"ו", "ֻ"}, get_form_medial(root_p), "ְ", get_form_initial(root_a))
           else
               uf_ = append_parts({"ו", "ָ"}, get_form_medial(root_p), "ֳ", get_form_medial(root_a))
           end
       else
           uf_ = append_parts({"ו", "ֻ"}, get_form_medial(root_p), "ְ", get_form_initial(root_a))
       end
       args["imp"] = args["imp"] or "-"
       args["fut_pre_stem"] = args["fut_pre_stem"] or uf_
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_fut_stem_endings(args, root_l, fut_char, ends_in_guttural(root_a))
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מ", uf_)
       get_pres_stem_endings(args, root_l, pres_char, ends_in_guttural(root_a), true, true)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts("ה", uf_)
       get_past_stem_endings(args, root_l, past_char, ends_in_guttural(root_a), false, true)
   end

end

get_stems_for["nif"] = function(args, categories)

   local root_p = args["root_p"]
   local root_a = args["root_a"]
   local root_l = args["root_l"]
   local past_char = args["past_char"] or "a"
   local fut_char = args["fut_char"] or gutturals[root_l] and root_l ~= "א" and "a" or "i"
   local imp_char = args["imp_char"] or fut_char
   local inf_char = args["inf_char"] or fut_char
   local pres_char = args["pres_char"] or "a"
   if false then
       -- reserved for special cases
   elseif false then
       -- reserved for special cases
   else
       local if_ = nil
       local suf_if_ = nil
       if args["assim"] ~= "-" then
           if_ = get_form_double(root_a, "i")
       elseif monophthongize[root_p] then
           if_ = append_parts(monophthongize[root_p], get_form_medial(root_a))
       elseif gutturals[root_p] then
           local v1 = root_p == "ע" and root_l == "י" and "ַ" or "ֶ"
           if args["nochataf"] then
               if_ = append_parts(v1, get_form_medial(root_p), "ְ", get_form_initial(root_a))
           else
               local v2 = v1 == "ַ" and "ֲ" or "ֱ"
               if_ = append_parts(v1, get_form_medial(root_p), v2, get_form_medial(root_a))
               suf_if_ = append_parts(v1, get_form_medial(root_p), v1, get_form_medial(root_a))
           end
       else
           if_ = append_parts("ִ", get_form_medial(root_p), "ְ", get_form_initial(root_a))
       end
       args["inf"] = args["inf"] or append_parts("לְה", get_form_double(root_p, "i", true), "ָ", get_form_medial(root_a), root_l == "י" and "וֹת" or append_parts(char_vowel[inf_char], get_form_final(root_l, inf_char)))
       if root_l == "י" then
           args["noun"] = args["noun"] or append_parts("ה", get_form_double(root_p, "i", true), "ָ", get_form_medial(root_a), "וּת")
       else
           args["noun"] = args["noun"] or append_parts("ה", get_form_double(root_p, "i", true), "ָ", get_form_medial(root_a), shva_na(root_a), get_form_medial(root_l), "וּת")
       end
       args["imp_pre_stem"] = args["imp_pre_stem"] or append_parts("ה", get_form_double(root_p, "i", true), "ָ", get_form_medial(root_a))
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts(get_form_double(root_p, "i", true), "ָ", get_form_medial(root_a))
       args["fut_1sp_stem"] = args["fut_1sp_stem"] or append_parts(get_form_double(root_p, "e", true), "ָ", get_form_medial(root_a))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_imp_stem_endings(args, root_l, imp_char, ends_in_guttural(root_a))
       get_fut_stem_endings(args, root_l, fut_char, ends_in_guttural(root_a))
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("נ", if_)
       get_pres_stem_endings(args, root_l, pres_char, ends_in_guttural(root_a), true, true)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts("נ", if_)
       if suf_if_ then
           local pres_stem_name = root_l == "י" and "past_3fs_pre_stem" or "past_3_pre_stem"
           args[pres_stem_name] = args[pres_stem_name] or append_parts("נ", suf_if_)
       end
       get_past_stem_endings(args, root_l, past_char, ends_in_guttural(root_a), false, true)
   end

end

get_stems_for["hit"] = function(args, categories)

   local itp = get_form_itp(args["root_p"])
   local etp = get_form_etp(args["root_p"])
   local root_a = args["root_a"]
   local root_l = args["root_l"]
   local past_char = args["past_char"] or "i"
   local fut_char = args["fut_char"] or "i"
   local imp_char = args["imp_char"] or fut_char
   local inf_char = args["inf_char"] or fut_char
   local pres_char = args["pres_char"] or "i"
   if false then
       -- reserved for special cases
   elseif false then
       -- reserved for special cases
   else
       args["inf"] = args["inf"] or append_parts("לְהִ", itp, get_form_double(root_a, "a"), root_l == "י" and "וֹת" or append_parts(char_vowel[inf_char], get_form_final(root_l, inf_char)))
       if root_l == "י" then
           args["noun"] = args["noun"] or append_parts("הִ", itp, get_form_double(root_a, "a"), "וּת")
       else
           args["noun"] = args["noun"] or append_parts("הִ", itp, get_form_double(root_a, "a"), shva_na(root_a), get_form_medial(root_l), "וּת")
       end
       args["imp_pre_stem"] = args["imp_pre_stem"] or append_parts("הִ", itp, get_form_double(root_a, "a"))
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts("ִ", itp, get_form_double(root_a, "a"))
       args["fut_1sp_stem"] = args["fut_1sp_stem"] or append_parts("ֶ", etp, get_form_double(root_a, "a"))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_imp_stem_endings(args, root_l, imp_char, ends_in_guttural(root_a))
       get_fut_stem_endings(args, root_l, fut_char, ends_in_guttural(root_a))
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מִ", itp, get_form_double(root_a, "a"))
       get_pres_stem_endings(args, root_l, pres_char, ends_in_guttural(root_a), true, false)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts("הִ", itp, get_form_double(root_a, "a"))
       get_past_stem_endings(args, root_l, past_char, ends_in_guttural(root_a), false, true)
   end

end

get_stems_for["hitpo"] = function(args, categories)

   local itp = get_form_itp(args["root_p"])
   local etp = get_form_etp(args["root_p"])
   local root_a = args["hollow"] == "-" and args["root_a"] or args["root_l"]
   local root_l = args["root_l"]
   local past_char = args["past_char"] or "i"
   local fut_char = args["fut_char"] or "i"
   local imp_char = args["imp_char"] or fut_char
   local inf_char = args["inf_char"] or fut_char
   local pres_char = args["pres_char"] or "i"
   if false then
       -- reserved for special cases
   elseif false then
       -- reserved for special cases
   else
       args["inf"] = args["inf"] or append_parts("לְהִ", itp, "וֹ", get_form_medial(root_a), root_l == "י" and "וֹת" or append_parts(char_vowel[inf_char], get_form_final(root_l, inf_char)))
       if root_l == "י" then
           args["noun"] = args["noun"] or append_parts("הִ", itp, "וֹ", get_form_medial(root_a), "וּת")
       else
           args["noun"] = args["noun"] or append_parts("הִ", itp, "וֹ", get_form_medial(root_a), shva_na(root_a), get_form_medial(root_l), "וּת")
       end
       args["imp_pre_stem"] = args["imp_pre_stem"] or append_parts("הִ", itp, "וֹ", get_form_medial(root_a))
       args["fut_pre_stem"] = args["fut_pre_stem"] or append_parts("ִ", itp, "וֹ", get_form_medial(root_a))
       args["fut_1sp_stem"] = args["fut_1sp_stem"] or append_parts("ֶ", etp, "וֹ", get_form_medial(root_a))
       args["fut_mid_stem"] = args["fut_mid_stem"] or ""
       get_imp_stem_endings(args, root_l, imp_char, ends_in_guttural(root_a))
       get_fut_stem_endings(args, root_l, fut_char, ends_in_guttural(root_a))
       args["pres_pre_stem"] = args["pres_pre_stem"] or append_parts("מִ", itp, "וֹ", get_form_medial(root_a))
       get_pres_stem_endings(args, root_l, pres_char, ends_in_guttural(root_a), true, false)
       args["past_pre_stem"] = args["past_pre_stem"] or append_parts("הִ", itp, "וֹ", get_form_medial(root_a))
       get_past_stem_endings(args, root_l, past_char, ends_in_guttural(root_a), false, true)
   end

end

get_stems_for["hitpu"] = function(args, categories)

   error("Binyan hitpu`al is not yet implemented.")

end

local form_names = {

       ["inf"] = true,
       ["noun"] = true,
       ["pp"] = true,
       ["imp_ms"] = true,
       ["imp_fs"] = true,
       ["imp_mp"] = true,
       ["imp_fp"] = true,
       ["fut_1s"] = true,
       ["fut_2ms"] = true,
       ["fut_2fs"] = true,
       ["fut_3ms"] = true,
       ["fut_3fs"] = true,
       ["fut_1p"] = true,
       ["fut_2mp"] = true,
       ["fut_2fp"] = true,
       ["fut_3mp"] = true,
       ["fut_3fp"] = true,
       ["past_1s"] = true,
       ["past_2ms"] = true,
       ["past_2fs"] = true,
       ["past_3ms"] = true,
       ["past_3fs"] = true,
       ["past_1p"] = true,
       ["past_2mp"] = true,
       ["past_2fp"] = true,
       ["past_3mp"] = true,
       ["past_3fp"] = true,
       ["pres_ms"] = true,
       ["pres_fs"] = true,
       ["pres_mp"] = true,
       ["pres_fp"] = true,

}

local optional_forms = {

       ["inf"] = true,
       ["noun"] = true,
       ["pp"] = true,

}

local table_template = [===[

]===]

local non_finite = {

   ["inf"] = "* to-infinitive: {inf}\n",
   ["noun"] = "* action noun: {noun}\n",
   ["pp"] = "* passive participle: {pp}\n",

}

local function make_table(args)

   for key, _ in pairs(form_names) do
       args[key] = args[key] and gen_link(args[key]) or (not optional_forms[key]) and "—"
   end
   args["non_finite"] = m_strutils.format(non_finite["inf"], args)
   if args["noun"] then
       args["non_finite"] = args["non_finite"] .. m_strutils.format(non_finite["noun"], args)
   end
   if args["pp"] then
       args["non_finite"] = args["non_finite"] .. m_strutils.format(non_finite["pp"], args)
   end
   return m_strutils.format(table_template, args)

end

local function track(binyan, args)

   local sub = nil
   local hollow = args["hollow"] ~= "-"
   local assim = args["assim"] ~= "-"
   local elide = args["elide"] ~= "-" and binyan == "pa" -- elide is only relevant to pa`al I think
   local geminate = args["geminate"] ~= "-"
   if binyan == "pa" or binyan == "nif" or binyan == "hif" or binyan == "huf" then
       if hollow then
           if assim or elide or geminate then
               sub = "?"
           else
               sub = "hollow"
           end
       elseif elide then
           if geminate then
               sub = "?"
           elseif assim then
               sub = "assim+elide"
           else
               sub = "elide"
           end
       elseif geminate then
           if assim then
               sub = "geminate+assim"
           else
               sub = "geminate"
           end
       elseif assim then
           sub = "assim"
       end
   end
   local m_debug = require('Module:debug')
   m_debug.track("he-conj/" .. binyan .. (sub and ("/" .. sub) or ""))
   if sub == "?" then
       m_debug.track("he-conj/incompatible-combination")
   end
   if args["past_long"] and args["past_long"] ~= "-" then
       m_debug.track("he-conj/past_long")
   end
   if args["past_long"] and args["fp_long"] ~= "-" then
       m_debug.track("he-conj/fp_long")
   end

end

local function track_page(lemma)

   local namespace = mw.title.getCurrentTitle().nsText
   if namespace == "" then
       local pagename = mw.title.getCurrentTitle().text
       local is_table = (type(lemma) == "table")
       local male = (lang:makeEntryName(is_table and lemma[1] or lemma))
       if pagename ~= male then
           local haser = (lang:makeEntryName(is_table and lemma[2] or lemma))
           local m_debug = require('Module:debug')
           if pagename ~= haser then
               m_debug.track("he-conj/not_at_lemma")
           else
               m_debug.track("he-conj/at_haser_spelling")
           end
       end
   end

end

-- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame)

   local args = frame:getParent().args
   local binyan = args[1] or frame.args[1] or error("Binyan has not been specified. Please pass parameter 1 to the module invocation or parent template.")
   -- PAGENAME = mw.title.getCurrentTitle().text
   -- NAMESPACE = mw.title.getCurrentTitle().nsText
   local categories = {}
   parse_root(args)
   convert_root(args)
   local root_p = args["root_p"]
   local root_a = args["root_a"]
   local root_l = args["root_l"]
   if not (root_p and root_a and root_l) then
       error("Missing root letters.")
   end
   args["hollow"] = args["hollow"] or ((root_a == "י" or root_a == "ו") and root_l ~= "י" and "+") or "-"
   args["geminate"] = args["geminate"] or ((root_a == root_l) and root_l ~= "י" and "+") or "-"
   args["assim"] = args["assim"] or (args["hollow"] == "-" and args["geminate"] == "-" and root_p == "נ" and not gutturals[root_a] and "+") or "-"
   args["elide"] = args["elide"] or (args["geminate"] and (args["assim"] ~= "-" == "-" or root_p == "י" or root_p == "ו") and root_l ~= "י" and "+") or "-"
   track(binyan, args)
   if binyan == "-" then
       -- do nothing, stems are given explicitly
   elseif get_stems_for[binyan] then
       get_stems_for[binyan](args, categories)
   else
       error("Unknown binyan code '" .. binyan .. "'")
   end
   conjugate(args) -- finishes conjugation based on stems from get_stems_for[binyan]
   local pre_notes = ""
   if args["past_2mp2"] and not equal(args["past_2mp"], args["past_2mp2"]) then
       args["2p_tag"] = "1"
       args["fp_tag"] = "2"
       pre_notes = pre_notes .. "# Pronounced " .. gen_link(args["past_2mp2"]) .. " and " .. gen_link(args["past_2fp2"]) .. " in informal Modern Hebrew.\n# Rare in Modern Hebrew.\n"
   else
       args["2p_tag"] = ""
       args["fp_tag"] = "1"
       pre_notes = pre_notes .. "# Rare in Modern Hebrew.\n"
   end
   args["notes"] = pre_notes .. (args["notes"] or "")
   if args["bot"] or "" ~= "" then
       return make_bot_list(forms, sep ~= "")
   else
       track_page(args["past_3ms"])
       return make_table(args, title) .. m_utilities.format_categories(categories, lang)
   end

end

return export