Jump to content

Module:he-verb

ពីWiktionary

Documentation for this module may be created at Module:he-verb/doc

--[=[

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
        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

        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_stem

        past_gem
        past_char *characteristic vowel [a,i,u], pa`al only (default: a)
        past_stem
        past_3_stem
        past_2p_stem

        hit_assim *assimilation or metathesis of the t- prefix in hitpa`el

]=]

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").getLanguageByCode("he")

-- 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_roots(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]

    args["notes"] = args["notes"] or ""
    args["notes"] = "# Uncommon in Modern Hebrew.\n"

    if args["bot"] or "" ~= "" then
        return make_bot_list(forms, sep ~= "")
    else
        return make_table(args, title) .. m_utilities.format_categories(categories, lang)
    end
end

local convert_root_char = {
    ["א"] = "א",
    ["ב"] = "ב",
    ["בּ"] = "ב", -- remove dagesh
    ["ג"] = "ג",
    ["גּ"] = "ג", -- remove dagesh
    ["ד"] = "ד",
    ["דּ"] = "ד", -- remove dagesh
    ["ה"] = "י", -- make hollow
    ["הּ"] = "ה", -- remove mappiq
    ["ו"] = "ו",
    ["ז"] = "ז",
    ["ח"] = "ח",
    ["ט"] = "ט",
    ["י"] = "י",
    ["כ"] = "כ",
    ["כּ"] = "כ", -- remove dagesh
    ["ך"] = "כ", -- normalize final
    ["ךּ"] = "כ", -- remove dagesh, normalize final
    ["ל"] = "ל",
    ["מ"] = "מ",
    ["ם"] = "מ", -- normalize final
    ["נ"] = "נ",
    ["ן"] = "נ", -- normalize final
    ["ס"] = "ס",
    ["ע"] = "ע",
    ["פ"] = "פ",
    ["פּ"] = "פ", -- remove dagesh
    ["ף"] = "פ", -- normalize final
    ["ףּ"] = "פ", -- remove dagesh, normalize final
    ["צ"] = "צ",
    ["ץ"] = "צ", -- normalize final
    ["ק"] = "ק",
    ["ר"] = "ר",
    ["ש"] = "שׁ", -- assume shin
    ["שׁ"] = "שׁ", -- shin
    ["שׂ"] = "שׂ", -- sin
    ["ת"] = "ת",
    ["תּ"] = "ת", -- remove dagesh
}

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

local forms_initial = {
    ["ב"] = "בּ",
    ["ג"] = "גּ",
    ["ד"] = "דּ",
    ["כ"] = "כּ",
    ["פ"] = "פּ",
    ["ת"] = "תּ",
}

local non_doubling_letters = {
    ["א"] = true,
    ["ה"] = false,
    ["ח"] = false,
    ["ע"] = false,
    ["ר"] = true,
}

local forms_final = {
    ["ה"] = "הַּ",
    ["ח"] = "חַ",
    ["כ"] = "ךְ",
    ["מ"] = "ם",
    ["נ"] = "ן",
    ["ע"] = "עַ",
    ["פ"] = "ף",
    ["צ"] = "ץ",
}

local forms_final_a = {
    ["ה"] = "הּ",
    ["כ"] = "ךְ",
    ["מ"] = "ם",
    ["נ"] = "ן",
    ["פ"] = "ף",
    ["צ"] = "ץ",
}

local lengthen_vowel = {
    ["ִ"] = "ֵ",
    ["ֻ"] = "ֹ",
    ["ַ"] = "ָ",
}

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(pg, x[2], lang, "Hebr", nil, nil, {}, PAGENAME)
        else
            return m_links.full_link(pg, pg .. " \\ " .. x[2], lang, "Hebr", nil, nil, {}, PAGENAME)
        end
    else
        if x == "-" then
            return "—" -- m-dash
        else
            return m_links.full_link(x, nil, lang, "Hebr", nil, nil, {}, PAGENAME)
        end
    end
end

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

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

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

function ends_in_shva(x)
    if type(x) == "table" then
        x = x[2]
    end
    return mw.ustring.sub(x, -1) == "ְ"
end

function get_form_initial(letter)
    if mw.ustring.sub(letter, 1, 1) == ":" then
        return mw.ustring.sub(letter, 2)
    else
        return forms_initial[letter] or letter
    end
end

function get_form_medial(letter)
    if mw.ustring.sub(letter, 1, 1) == ":" then
        return mw.ustring.sub(letter, 2)
    else
        return letter
    end
end

function get_form_double(letter, vowel)
    vowel = vowel or ""
    if mw.ustring.sub(letter, 1, 1) == ":" then
        return vowel .. mw.ustring.sub(letter, 2)
    else
        local lengthen = non_doubling_letters[letter]
        if lengthen == nil then
            return vowel .. letter .. "ּ"
        elseif lengthen then
            return (lengthen_vowel[vowel] or vowel) .. letter
        else
            return vowel .. letter
        end
    end
end

function get_form_final(letter)
    if mw.ustring.sub(letter, 1, 1) == ":" then
        return mw.ustring.sub(letter, 2)
    else
        return forms_final[letter] or letter
    end
end

function get_form_final_a(letter)
    if mw.ustring.sub(letter, 1, 1) == ":" then
        return mw.ustring.sub(letter, 2)
    else
        return forms_final_a[letter] or letter
    end
end

function infer_hit_assim(args)
    if not args["hit_assim"] then
        local p = args["root_p"]
        args["hit_assim"] = hit_assim_table[p] or ("תְ" .. get_form_initial(p))
    end
end

function parse_roots(args)
    for _, x in ipairs({"p", "a", "l"}) do
        if not args[x] then
            error("Missing root letter '" .. x .. "'.")
        end
        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] = convert_root_char[args[x]]
                if not args[root_x] then
                    error("Unrecognized root letter '" .. args[x] .. "'.")
                end
            end
        end
    end
end

function conjugate(args)
    if not args["imp_ms"] then
        args["imp_ms"] =  append_parts(args["imp_pre_stem"], args["fut_mid_stem"], args["fut_fin_stem"])
    end
    if not args["imp_fs"] then
        args["imp_fs"] = append_parts(args["imp_prs_stem"] or args["imp_pre_stem"], args["fut_mid_stem"], args["fut_suf_stem"], "ִי")
    end
    if not args["imp_mp"] then
        args["imp_mp"] = append_parts(args["imp_prs_stem"] or args["imp_pre_stem"], args["fut_mid_stem"], args["fut_suf_stem"], "וּ")
    end
    if not args["imp_fp"] then
        args["imp_fp"] = append_parts(args["imp_pre_stem"], args["fut_mid_stem"], args["fut_ffp_stem"], "נָה")
    end

    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("תּ", args["fut_pre_stem"], args["fut_mid_stem"], 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("תּ", args["fut_pre_stem"], args["fut_mid_stem"], args["fut_ffp_stem"], "נָה")
    end

    if not args["pres_ms"] then
        args["pres_ms"] = args["pres_stem"]
    end
    if not args["pres_fs"] then
        args["pres_fs"] = append_parts(args["pres_stem"], "ָה")
    end
    if not args["pres_mp"] then
        args["pres_mp"] = append_parts(args["pres_stem"], "ִים")
    end
    if not args["pres_fp"] then
        args["pres_fp"] = append_parts(args["pres_stem"], "וֹת")
    end

    local dagesh = args["past_stem"] and ends_in_shva(args["past_stem"])
    args["past_2p_stem"] = args["past_2p_stem"] or args["past_stem"]
    local dagesh_2p = args["past_2p_stem"] and ends_in_shva(args["past_2p_stem"]) -- should be the same, but who knows...
    if not args["past_1s"] then
        args["past_1s"] = append_parts(args["past_stem"], dagesh and "תִּי" or "תִי")
    end
    if not args["past_2ms"] then
        args["past_2ms"] = append_parts(args["past_stem"], dagesh and "תָּ" or "תָ")
    end
    if not args["past_2fs"] then
        args["past_2fs"] = append_parts(args["past_stem"], dagesh and "תְּ" or "ת")
    end
    if not args["past_3ms"] then
        args["past_3ms"] = args["past_3_stem"]
    end
    if not args["past_3fs"] then
        args["past_3fs"] = append_parts(args["past_3_stem"], "ָה")
    end
    if not args["past_1p"] then
        args["past_1p"] = append_parts(args["past_stem"], "נוּ")
    end
    if not args["past_2mp"] then
        args["past_2mp"] = append_parts(args["past_2p_stem"], dagesh_2p and "תֶּם" or "תֶם")
    end
    if not args["past_2fp"] then
        args["past_2fp"] = append_parts(args["past_2p_stem"], dagesh_2p and "תֶּן" or "תֶן")
    end
    if not args["past_3mp"] then
        args["past_3mp"] = append_parts(args["past_3_stem"], "וּ")
    end
    if not args["past_3fp"] then
        args["past_3fp"] = args["past_3mp"]
    end

    args["inf"] = args["inf"] or "-"

    args["heading"] = "Conjugation table for " .. gen_link(args["past_3ms"])
end

local gutturals = {
    ["א"] = true,
    ["ה"] = true,
    ["ח"] = true,
    ["ע"] = true,
}

local char_vowel = {
    ["a"] = "ַ",
    ["i"] = "ִ",
    ["u"] = {"ו", "ֹ"},
}

local char_vowel_closed = {
    ["a"] = "ַ",
    ["i"] = "ַ",
    ["u"] = {"ו", "ֹ"},
}

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"]
    args["past_char"] = args["past_char"] or "a"
    args["fut_char"] = args["fut_char"] or (gutturals[args["root_l"]] and "a") or "u"
    local past_char = args["past_char"]
    local fut_char = args["fut_char"]

    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_initial(root_a), {"ו", "ֹ"}, get_form_final(root_l))
        args["noun"] = args["noun"] or append_parts(get_form_initial(root_p), "ְ", get_form_medial(root_a), "ִי", 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))

        args["imp_pre_stem"] = args["imp_pre_stem"] or append_parts(get_form_initial(root_p), "ְ", get_form_medial(root_a))
        args["imp_prs_stem"] = args["imp_prs_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_initial(root_a))
        args["fut_1sp_stem"] = args["fut_1sp_stem"] or append_parts("ֶ", get_form_medial(root_p), "ְ", get_form_initial(root_a))
        args["fut_mid_stem"] = args["fut_mid_stem"] or ""
        args["fut_fin_stem"] = args["fut_fin_stem"] or append_parts(char_vowel[fut_char], fut_char == "a" and get_form_final_a(root_l) or get_form_final(root_l))
        args["fut_suf_stem"] = args["fut_suf_stem"] or append_parts("ְ", get_form_medial(root_l))
        args["fut_ffp_stem"] = args["fut_ffp_stem"] or append_parts(char_vowel[fut_char], get_form_medial(root_l), "ְ")

        args["pres_stem"] = args["pres_stem"] or append_parts(get_form_initial(root_p), "וֹ", get_form_medial(root_a), "ְ", get_form_medial(root_l))
        args["pres_ms"] = args["pres_ms"] or append_parts(get_form_initial(root_p), "וֹ", get_form_medial(root_a), "ֵ", get_form_final(root_l))
        args["pres_fs"] = args["pres_fs"] or append_parts(get_form_initial(root_p), "וֹ", get_form_medial(root_a), "ֶ", get_form_medial(root_l), "ֶת")

        args["past_3ms"] = args["past_3ms"] or append_parts(get_form_initial(root_p), "ָ", get_form_medial(root_a), char_vowel[past_char], past_char == "a" and get_form_final_a(root_l) or get_form_final(root_l))
        args["past_stem"] = args["past_stem"] or append_parts(get_form_initial(root_p), "ָ", get_form_medial(root_a), char_vowel_closed[past_char], get_form_medial(root_l), "ְ")
        args["past_2p_stem"] = args["past_2p_stem"] or append_parts(get_form_initial(root_p), "ְ", get_form_medial(root_a), char_vowel_closed[past_char], get_form_medial(root_l), "ְ")
        args["past_3_stem"] = args["past_3_stem"] or append_parts(get_form_initial(root_p), "ָ", get_form_medial(root_a), "ְ", get_form_medial(root_l))
    end

    -- error("Binyan pa`al is not yet implemented.")
end

get_stems_for["pi"] = function(args, categories)
    error("Binyan pi`el is not yet implemented.")
end

get_stems_for["po"] = function(args, categories)
    error("Binyan po`el is not yet implemented.")
end

get_stems_for["pu"] = function(args, categories)
    error("Binyan pu`al is not yet implemented.")
end

get_stems_for["hif"] = function(args, categories)
    error("Binyan hif`il is not yet implemented.")
end

get_stems_for["huf"] = function(args, categories)
    error("Binyan huf`al is not yet implemented.")
end

get_stems_for["nif"] = function(args, categories)
    error("Binyan nif`al is not yet implemented.")
end

get_stems_for["hit"] = function(args, categories)
    infer_hit_assim(args)
    error("Binyan hitpa`el is not yet implemented.")
end

get_stems_for["hitpo"] = function(args, categories)
    infer_hit_assim(args)
    error("Binyan hitpo`el is not yet implemented.")
end

get_stems_for["hitpu"] = function(args, categories)
    infer_hit_assim(args)
    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,
}

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

local table_template = [===[
<div class="NavFrame" style="width:100%m">
  <div class="NavHead" align="left">{heading}</div>
  <div class="NavContent" align="center">
    <table border="1" color="#cdcdcd" style="margin:1em; border-collapse:collapse; line-height:2em; border:2px solid #000000; background:#fdfdfd; text-align:center" cellpadding="10" class="inflection-table">
      <!-- note: widths of individual columns are controlled by the present-tense row -->

      <tr>
      <th scope='row' colspan="2" style="background:#E4C0CF;border-right:2px solid">non-finite forms</th>
      <td colspan="4" style="text-align:left">
        <ul>{non_finite}</ul>
      </td>
      </tr>

      <tr style="border-top:2px solid">
        <th colspan="2" style="background:#E4C0CF;border-right:2px solid">finite forms</th>
        <th scope='col' style="background:#C0CFE4">masculine<br/>singular</th>
        <th scope='col' style="background:#C0CFE4">feminine<br/>singular</th>
        <th scope='col' style="background:#C0CFE4">masculine<br/>plural</th>
        <th scope='col' style="background:#C0CFE4">feminine<br/>plural</th>
      </tr>

      <tr style="border-top:2px solid">
        <th scope='row' rowspan="3" style="background:#E2E4C0">past</th>
        <th scope='row' style="background:#C0CFE4;border-right:2px solid">first person</th>
        <td colspan="2">{past_1s}</td>
        <td colspan="2">{past_1p}</td>
      </tr>

      <tr>
        <th scope='row' style="background:#C0CFE4;border-right:2px solid">second person</th>
        <td>{past_2ms}</td>
        <td>{past_2fs}</td>
        <td>{past_2mp}</td>
        <td>{past_2fp}</td>
      </tr>

      <tr>
        <th scope='row' style="background:#C0CFE4;border-right:2px solid">third person</th>
        <td>{past_3ms}</td>
        <td>{past_3fs}</td>
        <td colspan="2">{past_3mp}</td>
      </tr>

      <tr style="border-top:2px solid">
        <th scope='row' style="background:#E2E4C0" width="16%">present</th>
        <th scope='row' style="background:#C0CFE4;border-right:2px solid" width="16%">all persons</th>
        <td width="17%">{pres_ms}</td>
        <td width="17%">{pres_fs}</td>
        <td width="17%">{pres_mp}</td>
        <td width="17%">{pres_fp}</td>
      </tr>

      <tr style="border-top:2px solid">
        <th scope='row' rowspan="3" style="background:#E2E4C0">future</th>
        <th scope='row' style="background:#C0CFE4;border-right:2px solid">first person</th>
        <td colspan="2">{fut_1s}</td>
        <td colspan="2">{fut_1p}</td>
      </tr>

      <tr>
        <th scope='row' style="background:#C0CFE4;border-right:2px solid">second person</th>
        <td>{fut_2ms}</td>
        <td>{fut_2fs}</td>
        <td>{fut_2mp}</td>
        <td>{fut_2fp}</td>
      </tr>

      <tr>
        <th scope='row' style="background:#C0CFE4;border-right:2px solid">third person</th>
        <td>{fut_3ms}</td>
        <td>{fut_3fs}</td>
        <td>{fut_3mp}</td>
        <td>{fut_3fp}</td>
      </tr>

      <tr style="border-top:2px solid">
        <th scope='row' style="background:#E2E4C0">imperative</th>
        <th scope='row' style="background:#C0CFE4;border-right:2px solid">second person</th>
        <td>{imp_ms}</td>
        <td>{imp_fs}</td>
        <td>{imp_mp}</td>
        <td>{imp_fp}</td>
      </tr>

      <tr style="border-top:2px solid">
        <th scope='row' colspan="2" style="background:#E4C0CF;border-right:2px solid">notes</th>
        <td colspan="4" style="text-align:left">
{notes}
        </td>
      </tr>

    </table>
  </div>
</div>
]===]

local non_finite = {
    ["inf"] = "<li>'''to-infinitive:''' {inf}</li>",
    ["noun"] = "<li>'''action noun:''' {noun}</li>",
    ["pp"] = "<li>'''passive participle:''' {pp}</li>",
}

function make_table(args)
    for key, _ in pairs(form_names) do
        args[key] = args[key] and gen_link(args[key])
    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

return export