Module:pnb-verb

local export = {}

--[=[

Authorship: Aryaman Arora , based on work by Ben Wing

]=]

--[=[ combine_stem_ending_tr TERMINOLOGY:

-- "slot" = A particular combination of person/number/gender/tense/etc. Example slot names for verbs are "inf_mp" (masculine plural infinitive), "prog" (undeclined progressive form), "pfv_ind_fut_2sm" (second-person singular	 masculine perfective indicative future). Each slot is filled with zero or more forms.

-- "form" = The conjugated Punjabi form representing the value of a given slot.

-- "lemma" = The dictionary form of a given Punjabi term. Generally the direct masculine singular infinitive, but may occasionally be another form if that form is missing. ]=]

--[=[

FIXME: ]=]

local lang = require("Module:languages").getByCode("pa") local m_table = require("Module:table") local m_links = require("Module:links") local m_string_utilities = require("Module:string utilities") local m_script_utilities = require("Module:script utilities") local iut = require("Module:inflection utilities") local m_para = require("Module:parameters") local com = require("Module:pa-common")

local u = mw.ustring.char local rsplit = mw.text.split local rfind = mw.ustring.find local rmatch = mw.ustring.match local rgmatch = mw.ustring.gmatch local rsubn = mw.ustring.gsub local ulen = mw.ustring.len local usub = mw.ustring.sub local uupper = mw.ustring.upper local ulower = mw.ustring.lower

-- vowel diacritics; don't display nicely on their own local AA = u(0x0a3e) local AI = u(0x0a48) local AU = u(0x0a4c) local E = u(0x0a47) local I = u(0x0a3f) local II = u(0x0a40) local O = u(0x0a4b) local U = u(0x0a41) local UU = u(0x0a42) local R = u(0x0a43) --nonexistent local VIRAMA = u(0x0a4d) local TILDE = u(0x0303)

local M = 'ੰ' local N = u(0x0a02) local EN = E .. N local IIN = II .. N local UUM = UU .. M

local function term_link(hi, tr) return m_links.full_link({term = hi, tr = tr, lang = lang}, "term") end

local sii_note = "[Some dialects conjugate " .. term_link("ਸੀ") .. " for person and number (from left to right): " .. term_link("ਸਾਂ") .. ", " .. term_link("ਸੈ") .. ", " .. term_link("ਸੋ") .. ", " .. term_link("ਸਾਂ") .. ", " .. term_link("ਸਨ") .. "]"

-- version of rsubn that discards all but the first return value local function rsub(term, foo, bar) local retval = rsubn(term, foo, bar) return retval end

-- version of rsubn that returns a 2nd argument boolean indicating whether -- a substitution was made. local function rsubb(term, foo, bar) local retval, nsubs = rsubn(term, foo, bar) return retval, nsubs > 0 end

local function tag_text(text) return m_script_utilities.tag_text(text, lang) end

local irreg_perf = { ["ਦੇ"] = "ਦਿੱਤ*", ["ਲੇ"] = "ਲਿੱਤ*", ["ਸੌ"] = "ਸੁੱਤ*", ["ਨਹਾਉ"] = "ਨਹਾਤ*", ["ਪੀ"] = "ਪੀਤ*", ["ਸੀ"] = "ਸੀਤ*", ["ਕਰ"] = "ਕੀਤ*", ["ਜਾ"] = "ਗ", ["ਖਾ"] = "ਖਾਦ*", ["ਮਰ"] = "ਮੋ", ["ਧੋ"] = "ਧੋਤ*", }

local irreg_subj = { }

local irreg_polite_imp = { }

local verb_slots_impers = { inf = "inf", inf_obl = "obl|inf", stem = "stem", conj = "conj|form", prog = "prog|form", }

local verb_slots_pers = { }

-- Add entries for a slot with only gender/number variants. -- `slot_prefix` is the prefix of the slot, typically specifying the tense/aspect; -- `tag_suffix` is the set of inflection tags to add after the gender/number tags, -- or "-" to use "-" as the inflection tags (which indicates that no accelerator entry -- should be generated); and `verb_slots` is the table (personal or impersonal) to -- add the entries to. local function add_slot_gendered(slot_prefix, tag_suffix, verb_slots) verb_slots = verb_slots or verb_slots_pers verb_slots[slot_prefix .. "_ms"] = tag_suffix == "-" and "-" or "m|s|" .. tag_suffix verb_slots[slot_prefix .. "_mp"] = tag_suffix == "-" and "-" or "m|p|" .. tag_suffix verb_slots[slot_prefix .. "_fs"] = tag_suffix == "-" and "-" or "f|s|" .. tag_suffix verb_slots[slot_prefix .. "_fp"] = tag_suffix == "-" and "-" or "f|p|" .. tag_suffix end

-- Same as add_slot_gendered but specifically for participles. This changes the inflection -- tags used, because the masculine singular entry is really only for direct masculine singular, -- and the masculine plural entry is also for oblique masculine singular. local function add_slot_gendered_part(slot_prefix, tag_suffix, verb_slots) verb_slots = verb_slots or verb_slots_pers verb_slots[slot_prefix .. "_ms"] = tag_suffix == "-" and "-" or "dir|m|s|" .. tag_suffix verb_slots[slot_prefix .. "_mp"] = tag_suffix == "-" and "-" or "m|p|" .. tag_suffix .. "|;|obl|m|s|" .. tag_suffix verb_slots[slot_prefix .. "_fs"] = tag_suffix == "-" and "-" or "f|s|" .. tag_suffix verb_slots[slot_prefix .. "_fp"] = tag_suffix == "-" and "-" or "f|p|" .. tag_suffix end

-- Compute the inflection tags associated with a given person/number/gender combination. local function personal_tags(slot_prefix, tag_suffix, persnum, gender) gender = gender and gender .. "|" or "" local suffix = gender .. tag_suffix if persnum == "2s" then -- only for imperatives return "2|s|intim|" .. suffix elseif persnum == "23s" then return "2|s|intim|" .. suffix .. "|;|3|s|" .. suffix elseif persnum == "1p" then return "1|p|" .. suffix .. "|;|2|formal|" .. suffix elseif persnum == "2p" then return "2|fam|" .. suffix elseif persnum == "3p" then return "3|p|" .. suffix .. "|;|2|formal|" .. suffix else -- 1s, 3s or 1p return persnum:gsub("^(.)(.)$", "%1|%2") .. "|" .. suffix end end

-- Return the possible person/number combinations given the slot prefix. local function persnum_values(slot_prefix) if slot_prefix:find("^imp_") then return {"2s", "2p", "3p"} else return {"1s", "23s", "1p", "3p", "2p"} end end

-- Add entries for a slot with only person/number variants. See `add_slot_gendered`. local function add_slot_personal(slot_prefix, tag_suffix, verb_slots) verb_slots = verb_slots or verb_slots_pers for _, persnum in ipairs(persnum_values(slot_prefix)) do local slot = slot_prefix .. "_" .. persnum if tag_suffix == "-" then verb_slots[slot] = "-" else verb_slots[slot] = personal_tags(slot_prefix, tag_suffix, persnum) end end end

-- Add entries for a slot with person/number/gender variants. See `add_slot_gendered`. local function add_slot_gendered_personal(slot_prefix, tag_suffix, verb_slots) verb_slots = verb_slots or verb_slots_pers for _, persnum in ipairs(persnum_values(slot_prefix)) do		for _, gender in ipairs({"m", "f"}) do local slot = slot_prefix .. "_" .. persnum .. gender if tag_suffix == "-" then verb_slots[slot] = "-" else verb_slots[slot] = personal_tags(slot_prefix, tag_suffix, persnum, gender) end end end end

add_slot_gendered_part("inf", "inf|part", verb_slots_impers) add_slot_gendered_part("hab", "hab|part", verb_slots_impers) add_slot_gendered_part("pfv", "pfv|part", verb_slots_impers) add_slot_gendered_part("agent", "prospective//agentive|part", verb_slots_impers) add_slot_gendered_part("adj_pfv", "-", verb_slots_impers) add_slot_gendered_part("adj_hab", "-", verb_slots_impers)

add_slot_personal("ind_pres", "pres|ind") -- only for होना add_slot_personal("ind_impf", "impf|ind") -- only for होना add_slot_gendered("ind_perf", "perf|ind") add_slot_gendered_personal("ind_fut", "fut|ind") add_slot_gendered_personal("presum", "presumptive") add_slot_personal("subj_pres", "pres|subj") -- only for होना add_slot_personal("subj_fut", "fut|subj") add_slot_gendered("cfact", "cfact") add_slot_personal("imp_reg", "reg|imp") add_slot_personal("imp_pol", "pol|imp")

add_slot_gendered_personal("hab_ind_pres", "-") add_slot_gendered("hab_ind_past", "-") add_slot_gendered_personal("hab_presum", "-") add_slot_gendered_personal("hab_subj", "-") add_slot_gendered("hab_cfact", "-")

for _, mood in ipairs({"pfv", "prog"}) do add_slot_gendered_personal(mood .. "_ind_pres", "-") add_slot_gendered(mood .. "_ind_past", "-") add_slot_gendered_personal(mood .. "_ind_fut", "-") add_slot_gendered_personal(mood .. "_presum", "-") add_slot_gendered_personal(mood .. "_subj_pres", "-") add_slot_gendered(mood .. "_cfact", "-") end

verb_slots_impers["inf_ms_linked"] = verb_slots_impers["inf_ms"]

local all_verb_slots = {} for k, v in pairs(verb_slots_impers) do	all_verb_slots[k] = v end for k, v in pairs(verb_slots_pers) do	all_verb_slots[k] = v end

-- Add one inflected form to `base.forms`, specifically to the list of forms associated with -- the slot `slot` in the table in `base.forms`. Each element of the list is an object of the -- form {form=FORM, translit=TRANSLIT, footnotes=FOOTNOTES}, where TRANSLIT is missing if no -- manual translit needs to be given and FOOTNOTES is missing if there aren't any footnotes. -- If FOOTNOTES is present it is a list of footnotes, where each footnote is e.g. "[rare or archaic]", -- i.e. surrounded by brackets, with the first character lowercase and no final period. -- (The brackets are automatically removed, the first character capitalized and a final period added.) -- -- `stem` is the Devanagari stem to add the ending to. -- `translit_stem` is the transliteration of `stem`, or nil to use the default transliteration. -- `ending` is the Devanagari ending to add to the stem, possibly with sandhi changes to the -- stem or ending. -- `footnotes` is a list of associated footnotes in the same format as FOOTNOTES above, or nil. -- `double_word` if given causes the resulting form to be doubled with a hyphen in between the two -- parts, for use with the progressive form (e.g. करते-करते of verb करना). local function add(base, stem, translit_stem, slot, ending, footnotes, double_word) local function doadd(new_stem, new_translit_stem, new_ending, slot_footnotes) new_ending = new_ending or ending if rfind(new_ending, "ਹਨ$") then slot_footnotes = iut.combine_footnotes(slot_footnotes, {"[colloquially, " .. term_link("ਹਨ") .. " is always " .. term_link("ਨੇ") .. " in the Majhi dialect]"}) end if rfind(new_ending, ".ਸੀ$") then slot_footnotes = iut.combine_footnotes(slot_footnotes, sii_note) end if new_ending and base.notlast then -- If we're not the last verb in a multiword expression, chop off -- anything after a space. This is to handle verbs like हिलना-डुलना, -- which have e.g. nonaspectual subjunctive 1sg masc हिलूँ-डुलूँ -- but perfective future indicative 1sg masc हिला-डुला हूँगा. Also, there -- are some special cases: -- (1) the conjunctive form should be e.g. हिल-डुलकर or हिल-डुलके -- (2) the future indicative should be e.g. 1sg हिलूँ-डुलूँगा -- (3) the future imperative 3pl should be e.g. हिलिये-डुलियेगा -- (4) the agentive should be e.g. हिलने-डुलनेवाला if slot == "conj" then new_ending = "" elseif slot:find("^ind_pol") then new_ending = rsub(new_ending, "ਗ.*$", "") -- गा, गे or गी elseif slot == "imp_pol_3p" then new_ending = rsub(new_ending, "गा$", "")--?? elseif slot:find("^agent") then new_ending = rsub(new_ending, "ਵਾਲ.*$", "") -- वाला, वाले, वाली, वालीं else new_ending = rsub(new_ending, " .*", "") end end com.add_form(base, new_stem or stem, new_translit_stem or translit_stem, slot,			new_ending, iut.combine_footnotes(slot_footnotes, footnotes), "link words", double_word) end if ending then if rfind(stem, "^ਗ$") then -- irregular ga stem ending = rsubn(ending, "^" .. E, "ਏ") ending = rsubn(ending, "^" .. II, "ਈ") ending = rsubn(ending, "^" .. I .. "ਆਂ", "ਇਆਂ") end if rfind(stem, "\*$") then -- some irregular stems should force -iā to turn into -ā stem = rsubn(stem, "\*$", "") if rfind(ending, "^" .. I .. "ਆ$") then doadd(stem, nil, AA) return elseif rfind(ending, "^" .. I .. "ਆ ") then local m = rsubn(ending, "^" .. I .. "ਆ", AA) doadd(stem, nil, m)				return end end if rfind(stem, "..[" .. U .. "ਉ]$") then if rfind(ending, "^[" .. AA .. I .. II .. U .. UU .. E .. AI .. O .. AU .. "]") or rfind(ending, "^ ") or rfind(ending, "^$") then stem = rsubn(stem, "[" .. U .. "ਉ]$", "") end end if rfind(ending, "^ਦ") and rfind(stem, "[" .. AA .. I .. II .. U .. UU .. E .. AI .. O .. AU .. "ਅਆਇਈਉਊਏਐਓਔ]$") and stem ~= "ਧੋ" then if rfind(stem, "^ਹੋ$") then stem = "ਹੁ" end if rfind(stem, "[" .. AA .. I .. U .. UU .. "ਅਆਇ]$") then doadd(stem .. M) else doadd(stem .. N) end return end if rfind(ending, "^[" .. AA .. E .. "]") and rfind(stem, "[" .. AA .. "ਆ]$") and not (slot:find('perf') or slot:find('pfv')) then doadd(stem .. "ਵ") if rfind(ending, "^" .. E) then local m = rsubn(ending, "^" .. E, "ਏ") doadd(stem, nil, m, {"[usually less common]"}) end return end if rfind(ending, "^[" .. O .. AA .. E .. "]") and rfind(stem, "[" .. O .. "ਓ]$") then doadd(stem .. "ਵ") return end if rfind(stem, "[" .. AI .. "ਐ]$") then if rfind(ending, "^" .. I .. "ਆ$") or rfind(ending, "^" .. I .. "ਆ ") then local n = rsubn(stem, AI .. "$", I) n = rsubn(n, "ਐ" .. "$", "ਇ") doadd(n, nil, AA) return elseif rfind(ending, "^[" .. AA .. E .. "]") and not (slot:find('perf') or slot:find('pfv')) then local n = rsubn(stem, "[" .. AI .. "ਐ]$", "") doadd(n .. "ਵ") if rfind(ending, "^" .. E) then local m = rsubn(ending, "^" .. E, "ਏ") doadd(n, nil, m, {"[usually less common]"}) end return elseif rfind(ending, "^[" .. AA .. I .. II .. U .. UU .. E .. AI .. O .. AU .. "]") then local n = rsubn(stem, "[" .. AI .. "ਐ]$", "") local m = rsubn(ending, "^[" .. AA .. I .. II .. U .. UU .. E .. AI .. O .. AU .. "]", com.diacritic_to_independent) doadd(n, nil, m)				return end end if rfind(stem, ".ਹਿ$") then if rfind(ending, "^" .. I .. "ਆ$") then local n = rsubn(stem, "ਹਿ$", I .. "ਹ") doadd(n, nil, AA) return elseif rfind(ending, "^" .. I .. "ਆ ") then local n = rsubn(stem, "ਹਿ$", I .. "ਹ") local m = rsubn(ending, "^" .. I .. "ਆ", AA) doadd(n, nil, m)				return elseif rfind(ending, "^[" .. AA .. I .. II .. U .. UU .. E .. AI .. O .. AU .. "]") then local n = rsubn(stem, "ਹਿ$", "ਹ") doadd(n) return end end if rfind(stem, "[ਰਣ]$") then if rfind(ending, "^ ?ਣ") then local n = rsubn(ending, "^ਣ", "ਨ") doadd(stem, nil, n)				return end end end

doadd end

-- Add the conjugation for a tense/aspect row with gender/number variants only. local function add_conj_gendered(base, slot_prefix, stem, translit_stem, m_s, m_p, f_s, f_p, footnotes) if not stem then stem = base.stem translit_stem = base.stem_translit end add(base, stem, translit_stem, slot_prefix .. "_ms", m_s, footnotes) add(base, stem, translit_stem, slot_prefix .. "_mp", m_p, footnotes) add(base, stem, translit_stem, slot_prefix .. "_fs", f_s, footnotes) add(base, stem, translit_stem, slot_prefix .. "_fp", f_p, footnotes) end

-- Add the conjugation for a tense/aspect row with person/number variants only. local function add_conj_personal(base, slot_prefix, stem, translit_stem, s1, s23, p1, p3, p2, footnotes) if not stem then stem = base.stem translit_stem = base.stem_translit end add(base, stem, translit_stem, slot_prefix .. "_1s", s1, footnotes) add(base, stem, translit_stem, slot_prefix .. "_23s", s23, footnotes) add(base, stem, translit_stem, slot_prefix .. "_1p", p1, footnotes) add(base, stem, translit_stem, slot_prefix .. "_3p", p3, footnotes) add(base, stem, translit_stem, slot_prefix .. "_2p", p2, footnotes) end

-- Add the conjugation for a tense/aspect row with person/number/gender variants. local function add_conj_gendered_personal(base, slot_prefix, stem, translit_stem,	s1m, s23m, p1m, p3m, p2m, s1f, s23f, p1f, p3f, p2f, footnotes) if not stem then stem = base.stem translit_stem = base.stem_translit end add(base, stem, translit_stem, slot_prefix .. "_1sm", s1m, footnotes) add(base, stem, translit_stem, slot_prefix .. "_23sm", s23m, footnotes) add(base, stem, translit_stem, slot_prefix .. "_1pm", p1m, footnotes) add(base, stem, translit_stem, slot_prefix .. "_3pm", p3m, footnotes) add(base, stem, translit_stem, slot_prefix .. "_2pm", p2m, footnotes) add(base, stem, translit_stem, slot_prefix .. "_1sf", s1f, footnotes) add(base, stem, translit_stem, slot_prefix .. "_23sf", s23f, footnotes) add(base, stem, translit_stem, slot_prefix .. "_1pf", p1f, footnotes) add(base, stem, translit_stem, slot_prefix .. "_3pf", p3f, footnotes) add(base, stem, translit_stem, slot_prefix .. "_2pf", p2f, footnotes) end

local function handle_derived_slots_and_overrides(base) -- No overrides implemented currently. -- process_slot_overrides(base)

-- Compute linked versions of potential lemma slots, for use in. -- We substitute the original lemma (before removing links) for forms that -- are the same as the lemma, if the original lemma has links. -- (NOTE: Not currently used by .) for _, slot in ipairs({"inf_ms"}) do iut.insert_forms(base.forms, slot .. "_linked", iut.map_forms(base.forms[slot], function(form, translit) if form == base.orig_lemma_no_links and translit == base.lemma_translit and rfind(base.orig_lemma, "%[%[") then return base.orig_lemma, base.lemma_translit else return form, translit end end)) end end

local conjs = {} local conjprops = {}

conjs["normal"] = function(base) local function fetch_irreg(irreg_table) if irreg_table[base.stem] then local stem = irreg_table[base.stem] return stem, nil else return base.stem, base.stem_translit end end

local perf, trperf = fetch_irreg(irreg_perf) local subj, trsubj = fetch_irreg(irreg_subj) local polite_imp, tr_polite_imp = fetch_irreg(irreg_polite_imp)

-- Undeclined forms add(base, base.stem, base.stem_translit, "stem", "") if rfind(base.stem, "[ੜਰ](੍ਹ)?$") then add(base, base.stem, base.stem_translit, "inf", "ਨਾ") add(base, base.stem, base.stem_translit, "inf_obl", "ਨ") else add(base, base.stem, base.stem_translit, "inf", "ਣਾ") add(base, base.stem, base.stem_translit, "inf_obl", "ਣ") end add(base, base.stem, base.stem_translit, "conj", "ਕੇ") -- Normally progressive form is doubled (e.g. करते-करते), but not in multiword -- expressions like हिलना-डुलना. add(base, base.stem, base.stem_translit, "prog", "ਦੇ", nil, not base.multiword)

-- Participles add_conj_gendered(base, "hab", nil, nil, "ਦਾ", "ਦੇ", "ਦੀ", "ਦਿਆਂ") add_conj_gendered(base, "pfv", perf, trperf, I .. "ਆ", E, II, I .. "ਆਂ") add_conj_gendered(base, "agent", nil, nil, "ਣ ਵਾਲ਼ਾ", "ਣ ਵਾਲ਼ੇ", "ਣ ਵਾਲ਼ੀ", "ਣ ਵਾਲ਼ਿਆਂ", "[many dialects do not have the " .. term_link("ਲ਼") .. " phoneme, instead replacing it with " .. term_link("ਲ") .. "]") add_conj_gendered(base, "adj_pfv", perf, trperf, I .. "ਆ ਹੋਇਆ", E .. " ਹੋਏ", II .. " ਹੋਈ", I .. "ਆਂ ਹੋਇਆਂ") add_conj_gendered(base, "adj_hab", nil, nil, "ਦਾ ਹੋਇਆ", "ਦੇ ਹੋਏ", "ਦੀ ਹੋਈ", "ਦਿਆਂ ਹੋਇਆਂ")

-- Non-aspectual add_conj_gendered(base, "ind_perf", perf, trperf, I .. "ਆ", E, II, I .. "ਆਂ") add_conj_personal(base, "subj_fut", subj, trsubj, AA .. N, E, AA .. N, "ਣ", O)	add_conj_gendered_personal(base, "ind_fut", subj, trsubj,		AA .. N .. "ਗਾ", E .. "ਗਾ", AA .. N .. "ਗੇ", "ਣਗੇ", O .. "ਗੇ",		AA .. N .. "ਗੀ", E .. "ਗੀ", AA .. N .. "ਗਿਆਂ", "ਣਗਿਆਂ", O .. "ਗਿਆਂ") add(base, subj, trsubj, "ind_fut_1sm", UU .. N .. "ਗਾ", "[dialectal]") add(base, subj, trsubj, "ind_fut_1sf", UU .. N .. "ਗੀ", "[dialectal]") add(base, subj, trsubj, "subj_fut_1s", UU .. N, "[dialectal]") if base.stem == "ਹੋ" then add_conj_gendered_personal(base, "presum", "", "", "ਹੋਵਾਂਗਾ", "ਹੋਵੇਗਾ", "ਹੋਵਾਂਗੇ", "ਹੋਣਗੇ", "ਹੋਵੋਗੇ", "ਹੋਵਾਂਗੀ", "ਹੋਵੇਗੀ", "ਹੋਵਾਂਗਿਆਂ", "ਹੋਣਗਿਆਂ", "ਹੋਵੋਗਿਆ") add_conj_personal(base, "ind_pres", "", "", "ਹਾਂ", "ਹੈ", "ਹਾਂ", "ਹਨ", "ਹੋ") add_conj_personal(base, "ind_impf", "", "", "ਸੀ", "ਸੀ", "ਸੀ", "ਸੀ", "ਸੀ") add_conj_personal(base, "ind_impf", "", "", "ਸਾਂ", "ਸੈ", "ਸਾਂ", "ਸਨ", "ਸੋ", "[dialectal]") add_conj_personal(base, "subj_pres", "", "", "ਹੋਵਾਂ", "ਹੋਵੇ", "ਹੋਇਏ", "ਹੋਣ", "ਹੋਵੋ") end add_conj_gendered(base, "cfact", nil, nil, "ਦਾ", "ਦੇ", "ਦੀ", "ਦਿਆਂ")

add(base, base.stem, base.stem_translit, "imp_reg_2s", "") add(base, subj, trsubj, "imp_reg_2p", O) add(base, subj, trsubj, "imp_pol_2s", II .. N) add(base, subj, trsubj, "imp_pol_2p", I .. "ਓ")

-- Habitual add_conj_gendered_personal(base, "hab_ind_pres", nil, nil,		"ਦਾ ਹਾਂ", "ਦਾ ਹੈ", "ਦੇ ਹਾਂ", "ਦੇ ਹਨ", "ਦੇ ਹੋ",		"ਦੀ ਹਾਂ", "ਦੀ ਹੈ", "ਦਿਆਂ ਹਾਂ", "ਦਿਆਂ ਹਨ", "ਦਿਆਂ ਹੋ") add_conj_gendered(base, "hab_ind_past", nil, nil, "ਦਾ ਸੀ", "ਦੇ ਸੀ", "ਦੀ ਸੀ", "ਦਿਆਂ ਸੀ") add_conj_gendered_personal(base, "hab_presum", nil, nil,		"ਦਾ ਹੋਵਾਂਗਾ", "ਦਾ ਹੋਵੇਗਾ", "ਦੇ ਹੋਵਾਂਗੇ", "ਦੇ ਹੋਣਗੇ", "ਦੇ ਹੋਵੋਗੇ",		"ਦੀ ਹੋਵਾਂਗੀ", "ਦੀ ਹੋਵੇਗੀ", "ਦਿਆਂ ਹੋਵਾਂਗਿਆਂ", "ਦਿਆਂ ਹੋਣਗਿਆਂ", "ਦਿਆਂ ਹੋਵੋਗਿਆਂ") add_conj_gendered_personal(base, "hab_subj", nil, nil,		"ਦਾ ਹੋਵਾਂ", "ਦਾ ਹੋਵੇ", "ਦੇ ਹੋਇਏ", "ਦੇ ਹੋਣ", "ਦੇ ਹੋਵੋ",		"ਦੀ ਹੋਵਾਂ", "ਦੀ ਹੋਵੇ", "ਦਿਆਂ ਹੋਇਏ", "ਦਿਆਂ ਹੋਣ", "ਦਿਆਂ ਹੋਵੋ") add_conj_gendered(base, "hab_cfact", nil, nil,		"ਦਾ ਹੁੰਦਾ", "ਦੇ ਹੁੰਦੇ", "ਦੀ ਹੁੰਦੀ", "ਦਿਆਂ ਹੁੰਦਿਆਂ")

-- Perfective add_conj_gendered_personal(base, "pfv_ind_pres", perf, trperf,		I .. "ਆ ਹਾਂ", I .. "ਆ ਹੈ", E .. " ਹਾਂ", E .. " ਹਨ", E .. " ਹੋ",		II .. " ਹਾਂ", II .. " ਹੈ", I .. "ਆਂ ਹਾਂ", I .. "ਆਂ ਹਨ", I .. "ਆਂ ਹੋ") add_conj_gendered(base, "pfv_ind_past", perf, trperf,		I .. "ਆ ਸੀ", E .. " ਸੀ",		II .. " ਸੀ", I .. "ਆਂ ਸੀ") add_conj_gendered_personal(base, "pfv_ind_fut", perf, trperf,		I .. "ਆ ਹੋਵਾਂਗਾ", I .. "ਆ ਹੋਵੇਗਾ", E .. " ਹੋਵਾਂਗੇ", E .. " ਹੋਣਗੇ", E .. " ਹੋਵੋਗੇ",		II .. " ਹੋਵਾਂਗਾ", II .. " ਹੋਵੇਗਾ", I .. "ਆਂ ਹੋਵਾਂਗੇ", I .. "ਆਂ ਹੋਣਗੇ", I .. "ਆਂ ਹੋਵੋਗੇ") add_conj_gendered_personal(base, "pfv_presum", perf, trperf,		I .. "ਆ ਹੋਵਾਂਗਾ", I .. "ਆ ਹੋਵੇਗਾ", E .. " ਹੋਵਾਂਗੇ", E .. " ਹੋਣਗੇ", E .. " ਹੋਵੋਗੇ",		II .. " ਹੋਵਾਂਗਾ", II .. " ਹੋਵੇਗਾ", I .. "ਆਂ ਹੋਵਾਂਗੇ", I .. "ਆਂ ਹੋਣਗੇ", I .. "ਆਂ ਹੋਵੋਗੇ") add_conj_gendered_personal(base, "pfv_subj_pres", perf, trperf,		I .. "ਆ ਹੋਵਾਂ", I .. "ਆ ਹੋਵੇ", E .. " ਹੋਇਏ", E .. " ਹੋਣ", E .. " ਹੋਵੋ",		II .. " ਹੋਵਾਂ", II .. " ਹੋਵੇ", I .. "ਆਂ ਹੋਇਏ", I .. "ਆਂ ਹੋਣ", I .. "ਆਂ ਹੋਵੋ") add_conj_gendered(base, "pfv_cfact", perf, trperf, I .. "ਆ ਹੁੰਦਾ", E .. " ਹੁੰਦੇ", II .. " ਹੁੰਦੀ", I .. "ਆਂ ਹੁੰਦਿਆਂ")

-- Progressive add_conj_gendered_personal(base, "prog_ind_pres", nil, nil,		" ਰਿਹਾ ਹਾਂ", " ਰਿਹਾ ਹੈ", " ਰਹੇ ਹਾਂ", " ਰਹੇ ਹਨ", " ਰਹੇ ਹੋ",		" ਰਹੀ ਹਾਂ", " ਰਹੀ ਹੈ", " ਰਹਿਆਂ ਹਾਂ", " ਰਹਿਆਂ ਹਨ", " ਰਹਿਆਂ ਹੋ") add_conj_gendered(base, "prog_ind_past", nil, nil,		" ਰਿਹਾ ਸੀ", " ਰਹੇ ਸੀ",		" ਰਹੀ ਸੀ", " ਰਹਿਆਂ ਸੀ") add_conj_gendered_personal(base, "prog_ind_fut", nil, nil,		" ਰਿਹਾ ਹੋਵਾਂਗਾ", " ਰਿਹਾ ਹੋਵੇਗਾ", " ਰਹੇ ਹੋਵਾਂਗੇ", " ਰਹੇ ਹੋਣਗੇ", " ਰਹੇ ਹੋਵੋਗੇ",		" ਰਹੀ ਹੋਵਾਂਗੀ", " ਰਹੀ ਹੋਵੇਗੀ", " ਰਹਿਆਂ ਹੋਵਾਂਗਿਆਂ", " ਰਹਿਆਂ ਹੋਣਗਿਆਂ", " ਰਹਿਆਂ ਹੋਵੋਗਿਆਂ") add_conj_gendered_personal(base, "prog_presum", nil, nil,		" ਰਿਹਾ ਹੋਵਾਂਗਾ", " ਰਿਹਾ ਹੋਵੇਗਾ", " ਰਹੇ ਹੋਵਾਂਗੇ", " ਰਹੇ ਹੋਣਗੇ", " ਰਹੇ ਹੋਵੋਗੇ",		" ਰਹੀ ਹੋਵਾਂਗੀ", " ਰਹੀ ਹੋਵੇਗੀ", " ਰਹਿਆਂ ਹੋਵਾਂਗਿਆਂ", " ਰਹਿਆਂ ਹੋਣਗਿਆਂ", " ਰਹਿਆਂ ਹੋਵੋਗਿਆਂ") add_conj_gendered_personal(base, "prog_subj_pres", nil, nil,		" ਰਿਹਾ ਹੋਵਾਂ", " ਰਿਹਾ ਹੋਵੇ", " ਰਹੇ ਹੋਇਏ", " ਰਹੇ ਹੋਣ", " ਰਹੇ ਹੋਵੋ",		" ਰਹੀ ਹੋਵਾਂ", " ਰਹੀ ਹੋਵੇ", " ਰਹਿਆਂ ਹੋਇਏ", " ਰਹਿਆਂ ਹੋਣ", " ਰਹਿਆਂ ਹੋਵੋ") add_conj_gendered(base, "prog_cfact", nil, nil, " ਰਿਹਾ ਹੁੰਦਾ", " ਰਹੇ ਹੁੰਦੇ", " ਰਹੀ ਹੁੰਦੀ", " ਰਹਿਆਂ ਹੁੰਦਿਆਂ") end

--[=[ Parse an indicator spec (text consisting of angle brackets and zero or more dot-separated indicators within them). Return value is an object of the form

{ forms = {}, -- forms for a single spec alternant; see `forms` below

-- The following additional fields are added by other functions: orig_lemma = "ORIGINAL-LEMMA", -- as given by the user or taken from pagename orig_lemma_no_links = "ORIGINAL-LEMMA-NO-LINKS", -- links removed lemma = "LEMMA", -- `orig_lemma_no_links`, converted to singular form if plural phon_lemma = "LEMMA-PHONETIC-RESPELLING", -- as specified by the user; may be missing lemma_translit = "LEMMA-TRANSLIT", -- translit of phon_lemma (if present) forms = { SLOT = { {		form = "FORM", footnotes = {"FOOTNOTE", "FOOTNOTE", ...} -- may be missing },	 ...	},	...  },  conj = "CONJ", -- declension, e.g. "normal" (the only one currently implemented) } ]=] local function parse_indicator_spec(angle_bracket_spec) local inside = rmatch(angle_bracket_spec, "^<(.*)>$") assert(inside) local base = {overrides = {}, forms = {}} if inside ~= "" then local segments = iut.parse_balanced_segment_run(inside, "[", "]") local dot_separated_groups = iut.split_alternating_runs(segments, "%.") for i, dot_separated_group in ipairs(dot_separated_groups) do -- No indicators allowed currently. local part = dot_separated_group[1] error("Unrecognized indicator '" .. part .. "': '" .. inside .. "'") end end return base end

local function detect_indicator_spec(base) base.conj = "normal" if rfind(base.lemma, "ਨਾ$") then base.lemma = rsubn(base.lemma, "ਨਾ$", "ਣਾ") end base.stem, base.stem_translit = com.strip_ending(base, "ਣਾ") end

local function detect_all_indicator_specs(alternant_multiword_spec) iut.map_word_specs(alternant_multiword_spec, function(base)		detect_indicator_spec(base)	end)

-- Set notlast=true on verbs that aren't the last one in a multiword expression, and -- multiword=true on all verbs in multiword expressions (as well as at top level), so -- we can properly handle verbs like हिलना-डुलना. for i, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do		if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do				for j, word_spec in ipairs(multiword_spec.word_specs) do					if j < #multiword_spec.word_specs then word_spec.notlast = true end if #multiword_spec.word_specs > 1 then word_spec.multiword = true alternant_multiword_spec.multiword = true end end end else if i < #alternant_multiword_spec.alternant_or_word_specs then alternant_or_word_spec.notlast = true end if #alternant_multiword_spec.alternant_or_word_specs > 1 then alternant_or_word_spec.multiword = true alternant_multiword_spec.multiword = true end end end end

local function conjugate_verb(base) if not conjs[base.conj] then error("Internal error: Unrecognized conjugation type '" .. base.conj .. "'") end conjs[base.conj](base) if base.multiword then -- See comment in add_variant_codes for the purpose of this. com.add_variant_codes(base) end handle_derived_slots_and_overrides(base) end

-- Compute the categories to add the verb to, as well as the annotation to display in the -- conjugation title bar. We combine the code to do these functions as both categories and -- title bar contain similar information. local function compute_categories_and_annotation(alternant_multiword_spec) local cats = {} local function insert(cattype) cattype = rsub(cattype, "~", alternant_multiword_spec.pos) m_table.insertIfNot(cats, "Punjabi " .. cattype) end local annotation if alternant_multiword_spec.manual then alternant_multiword_spec.annotation = "" else local function do_word_spec(base) if base.lemma_translit and (lang:transliterate(base.lemma)) ~= base.lemma_translit then insert("~ with phonetic respelling") end end iut.map_word_specs(alternant_multiword_spec, function(base)			do_word_spec(base)		end) end alternant_multiword_spec.categories = cats end

-- Convert forms from their list/object form (see the comments to add for how this works) -- to strings that can be directly filled into the table. Approximately, each form is converted -- to a formatted link with accelerators and the results are concatenated, followed by an newline -- and then the formatted transliterations. local function show_forms(alternant_multiword_spec) local lemmas = alternant_multiword_spec.forms.inf_ms or {} local props = { lemmas = lemmas, slot_table = verb_slots_impers, lang = lang, include_translit = true, }	if alternant_multiword_spec.multiword then -- Remove variant codes that were added to ensure only parallel variants in -- multiword expressions like हिलना-डुलना get generated. See com.add_variant_codes -- for more information. props.canonicalize = function(form) return com.remove_variant_codes(form) end end iut.show_forms(alternant_multiword_spec.forms, props) alternant_multiword_spec.forms.footnote_impers = alternant_multiword_spec.forms.footnote props.slot_table = verb_slots_pers iut.show_forms(alternant_multiword_spec.forms, props) alternant_multiword_spec.forms.footnote_pers = alternant_multiword_spec.forms.footnote end

-- Generate the conjugation table. This should be called after show_forms has converted -- each form to a formatted string. local function make_table(alternant_multiword_spec) local table_spec_impersonal = [=[ Impersonal forms of {inf_raw} {\op}| class="inflection-table inflection-hi inflection-verb" data-toggle-category="inflection" ! class="hi-tense-aspect-cell"colspan=100% | Undeclined ! class="hi-tense-aspect-cell" colspan=2 | Stem ! class="hi-tense-aspect-cell" colspan=2 | Infinitive ! class="hi-tense-aspect-cell" colspan=2 | Oblique Infinitive ! class="hi-tense-aspect-cell" colspan=2 | Conjunctive ! class="hi-tense-aspect-cell" colspan=2 | Progressive ! class="hi-tense-aspect-cell" colspan=100% | Participles ! {dir} {m} {s} ! {m} {p} {obl} {m} {s} ! {f} {s} ! {f} {p} ! class="hi-tense-aspect-cell" colspan=2 | Infinitive ! class="hi-tense-aspect-cell" colspan=2 | Habitual ! class="hi-tense-aspect-cell" colspan=2 | Perfective ! class="hi-tense-aspect-cell" colspan=2 | Prospective Agentive ! class="hi-tense-aspect-cell" rowspan=2 | Adjectival ! class="hi-tense-aspect-cell" | Perfective ! class="hi-tense-aspect-cell" | Habitual
 * colspan="100%" | {stem}
 * colspan="100%" | {inf}
 * colspan="100%" | {inf_obl}
 * colspan="100%" | {conj}
 * colspan="100%" | {prog}
 * - class="hi-part-gender-number-header"
 * class="hi-tense-aspect-cell" colspan=2 |
 * class="hi-tense-aspect-cell" colspan=2 |
 * {inf_ms}
 * {inf_mp}
 * {inf_fs}
 * {inf_fp}
 * {hab_ms}
 * {hab_mp}
 * {hab_fs}
 * {hab_fp}
 * {pfv_ms}
 * {pfv_mp}
 * {pfv_fs}
 * {pfv_fp}
 * {agent_ms}
 * {agent_mp}
 * {agent_fs}
 * {agent_fp}
 * {adj_pfv_ms}
 * {adj_pfv_mp}
 * {adj_pfv_fs}
 * {adj_pfv_fp}
 * {adj_hab_ms}
 * {adj_hab_mp}
 * {adj_hab_fs}
 * {adj_hab_fp}
 * {\cl}{notes_clause} ]=]

local person_number_header_two_row = [=[ ]=]
 * - class="hi-table-header"
 * rowspan=2 |
 * rowspan=2 |
 * class="hi-mf-cell" rowspan=2 |

local person_number_header_sg_pl_headers = [=[ ! colspan=3 | Singular ! colspan=1 | Singular/Plural ! colspan=2 | Plural/Formal ]=]

local person_number_header_table_div = [=[ ]=]
 * - class="hi-table-header"

local person_number_header_pers_num_headers = [=[ ! 1st ਮੈਂ ! 2nd intimate ਤੂੰ ! 3rd ਇਹ/ਉਹ ! 2nd familiar ਤੁਸੀਂ ! 1st ਅਸੀਂ ! 2nd formal, 3rd ਇਹ/ਉਹ/ਆਪ ]=]

-- Regular person-number header used at the top of the table and in the middle. local person_number_header = person_number_header_table_div .. person_number_header_two_row .. person_number_header_sg_pl_headers .. person_number_header_table_div .. person_number_header_pers_num_headers -- Reversed person-number header used at the bottom of the table. "Reversed" means that -- the two rows are in reversed order; but internally we can't switch the order of everything -- (e.g. the double-row cells at the left side), so we need to break up the header into multiple -- parts and only reverse certain parts. local reversed_person_number_header = person_number_header_table_div .. person_number_header_two_row .. person_number_header_pers_num_headers .. person_number_header_table_div .. person_number_header_sg_pl_headers

local table_spec_personal = [=[ Personal forms of {inf_raw} {\op}| class="inflection-table inflection-hi inflection-verb" data-toggle-category="inflection" ! class="hi-sec-div" rowspan=1 colspan=100% | Non-Aspectual {person_number_header}{pres_impf_table}! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PERF} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {FUT} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} {presum_table}{subj_table} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Contrafactual ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PST} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Imperative ! class="hi-tense-aspect-cell" rowspan=1 colspan=1 | {REG} ! class="hi-mf-cell" | {mf} ! class="hi-tense-aspect-cell" rowspan=1 colspan=1 | {POL} ! class="hi-mf-cell" | {mf} ! class="hi-sec-div" rowspan=1 colspan=100% | Habitual ! class="hi-tense-aspect-cell" rowspan=4 colspan=1 | Indicative ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PRS} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PST} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Presumptive ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PRS_PST} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Subjunctive ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PRS} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Contrafactual ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PST} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-sec-div" rowspan=1 colspan=100% | Perfective {person_number_header}|- class="hi-row-m" ! class="hi-tense-aspect-cell" rowspan=6 colspan=1 | Indicative ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PRS} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PST} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {FUT} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Presumptive ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PRS_PST} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Subjunctive ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PRS} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Contrafactual ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PST} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-sec-div" rowspan=1 colspan=100% | Progressive ! class="hi-tense-aspect-cell" rowspan=6 colspan=1 | Indicative ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PRS} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PST} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {FUT} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Presumptive ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PRS_PST_FUT} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Subjunctive ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PRS} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Contrafactual ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PST} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} {reversed_person_number_header}|{\cl}{notes_clause} ]=]
 * colspan=3 | {ind_perf_ms}
 * colspan=3 | {ind_perf_mp}
 * - class="hi-row-f"
 * colspan=3 | {ind_perf_fs}
 * colspan=3 | {ind_perf_fp}
 * - class="hi-row-m"
 * {ind_fut_1sm}
 * colspan=2 | {ind_fut_23sm}
 * {ind_fut_2pm}
 * {ind_fut_1pm}
 * {ind_fut_3pm}
 * - class="hi-row-f"
 * {ind_fut_1sf}
 * colspan=2 | {ind_fut_23sf}
 * {ind_fut_2pf}
 * {ind_fut_1pf}
 * {ind_fut_3pf}
 * - class="hi-row-m"
 * colspan=3 | {cfact_ms}
 * colspan=3 | {cfact_mp}
 * - class="hi-row-f"
 * colspan=3 | {cfact_fs}
 * colspan=3 | {cfact_fp}
 * {imp_reg_2s}
 * {imp_reg_2p}
 * {imp_reg_2p}
 * {imp_reg_2p}
 * {imp_reg_2p}
 * {imp_reg_2p}
 * {imp_pol_2s}
 * {imp_pol_2p}
 * {imp_pol_2p}
 * {imp_pol_2p}
 * {imp_pol_2p}
 * {imp_pol_2p}
 * - class="hi-row-m"
 * {hab_ind_pres_1sm}
 * colspan=2 | {hab_ind_pres_23sm}
 * {hab_ind_pres_2pm}
 * {hab_ind_pres_1pm}
 * {hab_ind_pres_3pm}
 * - class="hi-row-f"
 * {hab_ind_pres_1sf}
 * colspan=2 | {hab_ind_pres_23sf}
 * {hab_ind_pres_2pf}
 * {hab_ind_pres_1pf}
 * {hab_ind_pres_3pf}
 * - class="hi-row-m"
 * colspan=3 | {hab_ind_past_ms}
 * colspan=3 | {hab_ind_past_mp}
 * - class="hi-row-f"
 * colspan=3 | {hab_ind_past_fs}
 * colspan=3 | {hab_ind_past_fp}
 * - class="hi-row-m"
 * {hab_presum_1sm}
 * colspan=2 | {hab_presum_23sm}
 * {hab_presum_2pm}
 * {hab_presum_1pm}
 * {hab_presum_3pm}
 * - class="hi-row-f"
 * {hab_presum_1sf}
 * colspan=2 | {hab_presum_23sf}
 * {hab_presum_2pf}
 * {hab_presum_1pf}
 * {hab_presum_3pf}
 * - class="hi-row-m"
 * {hab_subj_1sm}
 * colspan=2 | {hab_subj_23sm}
 * {hab_subj_2pm}
 * {hab_subj_1pm}
 * {hab_subj_3pm}
 * - class="hi-row-f"
 * {hab_subj_1sf}
 * colspan=2 | {hab_subj_23sf}
 * {hab_subj_2pf}
 * {hab_subj_1pf}
 * {hab_subj_3pf}
 * - class="hi-row-m"
 * colspan=3 | {hab_cfact_ms}
 * colspan=3 | {hab_cfact_mp}
 * - class="hi-row-f"
 * colspan=3 | {hab_cfact_fs}
 * colspan=3 | {hab_cfact_fp}
 * {pfv_ind_pres_1sm}
 * colspan=2 | {pfv_ind_pres_23sm}
 * {pfv_ind_pres_2pm}
 * {pfv_ind_pres_1pm}
 * {pfv_ind_pres_3pm}
 * - class="hi-row-f"
 * {pfv_ind_pres_1sf}
 * colspan=2 | {pfv_ind_pres_23sf}
 * {pfv_ind_pres_2pf}
 * {pfv_ind_pres_1pf}
 * {pfv_ind_pres_3pf}
 * - class="hi-row-m"
 * colspan=3 | {pfv_ind_past_ms}
 * colspan=3 | {pfv_ind_past_mp}
 * - class="hi-row-f"
 * colspan=3 | {pfv_ind_past_fs}
 * colspan=3 | {pfv_ind_past_fp}
 * - class="hi-row-m"
 * {pfv_ind_fut_1sm}
 * colspan=2 | {pfv_ind_fut_23sm}
 * {pfv_ind_fut_2pm}
 * {pfv_ind_fut_1pm}
 * {pfv_ind_fut_3pm}
 * - class="hi-row-f"
 * {pfv_ind_fut_1sf}
 * colspan=2 | {pfv_ind_fut_23sf}
 * {pfv_ind_fut_2pf}
 * {pfv_ind_fut_1pf}
 * {pfv_ind_fut_3pf}
 * - class="hi-row-m"
 * {pfv_presum_1sm}
 * colspan=2 | {pfv_presum_23sm}
 * {pfv_presum_2pm}
 * {pfv_presum_1pm}
 * {pfv_presum_3pm}
 * - class="hi-row-f"
 * {pfv_presum_1sf}
 * colspan=2 | {pfv_presum_23sf}
 * {pfv_presum_2pf}
 * {pfv_presum_1pf}
 * {pfv_presum_3pf}
 * - class="hi-row-m"
 * {pfv_subj_pres_1sm}
 * colspan=2 | {pfv_subj_pres_23sm}
 * {pfv_subj_pres_2pm}
 * {pfv_subj_pres_1pm}
 * {pfv_subj_pres_3pm}
 * - class="hi-row-f"
 * {pfv_subj_pres_1sf}
 * colspan=2 | {pfv_subj_pres_23sf}
 * {pfv_subj_pres_2pf}
 * {pfv_subj_pres_1pf}
 * {pfv_subj_pres_3pf}
 * - class="hi-row-m"
 * colspan=3 | {pfv_cfact_ms}
 * colspan=3 | {pfv_cfact_mp}
 * - class="hi-row-f"
 * colspan=3 | {pfv_cfact_fs}
 * colspan=3 | {pfv_cfact_fp}
 * - class="hi-row-m"
 * {prog_ind_pres_1sm}
 * colspan=2 | {prog_ind_pres_23sm}
 * {prog_ind_pres_2pm}
 * {prog_ind_pres_1pm}
 * {prog_ind_pres_3pm}
 * - class="hi-row-f"
 * {prog_ind_pres_1sf}
 * colspan=2 | {prog_ind_pres_23sf}
 * {prog_ind_pres_2pf}
 * {prog_ind_pres_1pf}
 * {prog_ind_pres_3pf}
 * - class="hi-row-m"
 * colspan=3 | {prog_ind_past_ms}
 * colspan=3 | {prog_ind_past_mp}
 * - class="hi-row-f"
 * colspan=3 | {prog_ind_past_fs}
 * colspan=3 | {prog_ind_past_fp}
 * - class="hi-row-m"
 * {prog_ind_fut_1sm}
 * colspan=2 | {prog_ind_fut_23sm}
 * {prog_ind_fut_2pm}
 * {prog_ind_fut_1pm}
 * {prog_ind_fut_3pm}
 * - class="hi-row-f"
 * {prog_ind_fut_1sf}
 * colspan=2 | {prog_ind_fut_23sf}
 * {prog_ind_fut_2pf}
 * {prog_ind_fut_1pf}
 * {prog_ind_fut_3pf}
 * - class="hi-row-m"
 * {prog_presum_1sm}
 * colspan=2 | {prog_presum_23sm}
 * {prog_presum_2pm}
 * {prog_presum_1pm}
 * {prog_presum_3pm}
 * - class="hi-row-f"
 * {prog_presum_1sf}
 * colspan=2 | {prog_presum_23sf}
 * {prog_presum_2pf}
 * {prog_presum_1pf}
 * {prog_presum_3pf}
 * - class="hi-row-m"
 * {prog_subj_pres_1sm}
 * colspan=2 | {prog_subj_pres_23sm}
 * {prog_subj_pres_2pm}
 * {prog_subj_pres_1pm}
 * {prog_subj_pres_3pm}
 * - class="hi-row-f"
 * {prog_subj_pres_1sf}
 * colspan=2 | {prog_subj_pres_23sf}
 * {prog_subj_pres_2pf}
 * {prog_subj_pres_1pf}
 * {prog_subj_pres_3pf}
 * - class="hi-row-m"
 * colspan=3 | {prog_cfact_ms}
 * colspan=3 | {prog_cfact_mp}
 * - class="hi-row-f"
 * colspan=3 | {prog_cfact_fs}
 * colspan=3 | {prog_cfact_fp}

local pres_impf_table = [=[ ! class="hi-tense-aspect-cell" rowspan=6 colspan=1 | Indicative ! class="hi-tense-aspect-cell" rowspan=1 colspan=1 | {PRS} ! class="hi-mf-cell" | {mf} ! class="hi-tense-aspect-cell" colspan=1 | {IMPF} ! class="hi-mf-cell" | {mf} ]=]
 * {ind_pres_1s}
 * colspan=2 | {ind_pres_23s}
 * {ind_pres_2p}
 * {ind_pres_1p}
 * {ind_pres_3p}
 * - class="hi-row-m"
 * {ind_impf_1s}
 * colspan=2 | {ind_impf_23s}
 * {ind_impf_2p}
 * {ind_impf_1p}
 * {ind_impf_3p}
 * - class="hi-row-m"

local pres_impf_table_missing = [=[ ! class="hi-tense-aspect-cell" rowspan=4 colspan=1 | Indicative ]=]
 * - class="hi-row-m"

local presum_table = [=[ ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | Presumptive ! class="hi-tense-aspect-cell" rowspan=2 colspan=1 | {PRS_PST_FUT} ! class="hi-mf-cell" | {m} ! class="hi-mf-cell" | {f} ]=]
 * - class="hi-row-m"
 * {presum_1sm}
 * colspan=2 | {presum_23sm}
 * {presum_2pm}
 * {presum_1pm}
 * {presum_3pm}
 * - class="hi-row-f"
 * {presum_1sf}
 * colspan=2 | {presum_23sf}
 * {presum_2pf}
 * {presum_1pf}
 * {presum_3pf}

local combined_subj = [=[ ! class="hi-tense-aspect-cell" rowspan=1 colspan=1 | Subjunctive ! class="hi-tense-aspect-cell" rowspan=1 colspan=1 | {FUT} ! class="hi-mf-cell" | {mf}
 * {subj_fut_1s}
 * colspan=2 | {subj_fut_23s}
 * {subj_fut_2p}
 * {subj_fut_1p}
 * {subj_fut_3p}]=]

local split_subj = [=[ ! class="hi-tense-aspect-cell" rowspan=1 colspan=1 | Subjunctive ! class="hi-tense-aspect-cell" rowspan=1 colspan=1 | {PRS} ! class="hi-mf-cell" | {mf}
 * {subj_pres_1s}
 * colspan=2 | {subj_pres_23s}
 * {subj_pres_2p}
 * {subj_pres_1p}
 * {subj_pres_3p}]=]

local notes_template = [===[ {footnote} ]===]

local forms = alternant_multiword_spec.forms

local function make_gender_abbr(title, text) return ' ' .. text .. ' '	end local function make_tense_aspect_abbr(title, text) local template = [=[ {text} ]=] return m_string_utilities.format(template, {title = title, text = text}) end forms.m = make_gender_abbr("masculine gender", "m") forms.f = make_gender_abbr("feminine gender", "f") forms.mf = forms.m .. " " .. forms.f	forms.s = make_gender_abbr("singular number", "s") forms.p = make_gender_abbr("plural number", "p") forms.dir = make_gender_abbr("direct", "dir") forms.obl = make_gender_abbr("oblique", "obl") forms.PERF = make_tense_aspect_abbr("Perfect", "PERF") forms.IMPF = make_tense_aspect_abbr("Imperfect", "IMPF") forms.PST = make_tense_aspect_abbr("Past", "PST") forms.FUT = make_tense_aspect_abbr("Future", "FUT") forms.PRS = make_tense_aspect_abbr("Present", "PRS") forms.PRS_FUT = make_tense_aspect_abbr("Present/Future", "PRS FUT") forms.PRS_PST = make_tense_aspect_abbr("Present/Past", "PRS PST") forms.PRS_PST_FUT = make_tense_aspect_abbr("Present/Past/Future", "PRS PST FUT") forms.REG = make_tense_aspect_abbr("Regular", "REG") forms.POL = make_tense_aspect_abbr("Polite", "POL") forms.inf_raw = tag_text(forms.lemma)

-- Now format the impersonal table. forms.footnote = forms.footnote_impers forms.notes_clause = forms.footnote ~= "" and m_string_utilities.format(notes_template, forms) or "" local formatted_table_impers = m_string_utilities.format(table_spec_impersonal, forms)

-- Now format the personal table. forms.footnote = forms.footnote_pers forms.notes_clause = forms.footnote ~= "" and m_string_utilities.format(notes_template, forms) or "" if forms.ind_pres_1s ~= "—" then -- होना forms.subj_table = m_string_utilities.format(split_subj, forms) forms.pres_impf_table = m_string_utilities.format(pres_impf_table, forms) forms.presum_table = m_string_utilities.format(presum_table, forms) else forms.subj_table = m_string_utilities.format(combined_subj, forms) forms.pres_impf_table = pres_impf_table_missing forms.presum_table = "" end forms.person_number_header = person_number_header forms.reversed_person_number_header = reversed_person_number_header local formatted_table_pers = m_string_utilities.format(table_spec_personal, forms)

-- Concatenate both. return require("Module:TemplateStyles")("Module:hi-verb/style.css") .. formatted_table_impers .. formatted_table_pers end

-- Implementation of template 'pa-verb cat'. -- NOTE: Not currently used. function export.catboiler(frame) local SUBPAGENAME = mw.title.getCurrentTitle.subpageText local params = { [1] = {},	}	local args = m_para.process(frame:getParent.args, params)

local function get_pos local pos = rmatch(SUBPAGENAME, "^Punjabi.- ([^ ]*)s ") if not pos then pos = rmatch(SUBPAGENAME, "^Punjabi.- ([^ ]*)s$") end if not pos then error("Invalid category name, should be e.g. \"Punjabi verbs with ...\" or \"Punjabi ... verbs\"") end return pos end

local function get_sort_key local pos, sort_key = rmatch(SUBPAGENAME, "^Punjabi.- ([^ ]*)s with (.*)$") if sort_key then return sort_key end pos, sort_key = rmatch(SUBPAGENAME, "^Punjabi ([^ ]*)s (.*)$") if sort_key then return sort_key end return rsub(SUBPAGENAME, "^Punjabi ", "") end

local cats = {}, pos

-- Insert the category CAT (a string) into the categories. String will -- have "Punjabi " prepended and ~ substituted for the plural part of speech. local function insert(cat, atbeg) local fullcat = "Punjabi " .. rsub(cat, "~", pos .. "s") if atbeg then table.insert(cats, 1, fullcat) else table.insert(cats, fullcat) end end

local maintext while true do		if args[1] then maintext = "~ " .. args[1] pos = get_pos break end

error("Unrecognized Punjabi verb category name") end

insert("~|" .. get_sort_key, "at beginning")

local categories = {} for _, cat in ipairs(cats) do		table.insert(categories, "") end

return "This category contains Punjabi " .. rsub(maintext, "~", pos .. "s") .. "\n" .. mw.getCurrentFrame:expandTemplate{title="hi-categoryTOC", args={}} .. table.concat(categories, "") end

-- Externally callable function to parse and conjugate a verb given user-specified arguments. -- Return value is WORD_SPEC, an object where the conjugated forms are in `WORD_SPEC.forms` -- for each slot. If there are no values for a slot, the slot key will be missing. The value -- for a given slot is a list of objects {form=FORM, translit=TRANSLIT, footnotes=FOOTNOTES}. function export.do_generate_forms(parent_args, pos, from_headword, def) local params = { [1] = {},		footnote_impers = {list = true}, footnote_pers = {list = true}, title = {}, }

if from_headword then params["lemma"] = {list = true} params["id"] = {} end

local args = m_para.process(parent_args, params) local PAGENAME = mw.title.getCurrentTitle.text

if not args[1] then if PAGENAME == "pa-conj" then args[1] = def or "ਵੇਖਣਾ" else args[1] = PAGENAME -- If pagename has spaces in it, add links around each word if args[1]:find(" ") then args[1] = "" .. rsub(args[1], " ", " ") .. "" end end end local parse_props = { parse_indicator_spec = parse_indicator_spec, lang = lang, transliterate_respelling = com.transliterate_respelling, allow_default_indicator = true, allow_blank_lemma = true, }	local alternant_multiword_spec = iut.parse_inflected_text(args[1], parse_props) alternant_multiword_spec.title = args.title alternant_multiword_spec.footnotes_impers = args.footnote_impers alternant_multiword_spec.footnotes_pers = args.footnote_pers alternant_multiword_spec.pos = pos or "verbs" alternant_multiword_spec.args = args com.normalize_all_lemmas(alternant_multiword_spec) detect_all_indicator_specs(alternant_multiword_spec) local inflect_props = { slot_table = all_verb_slots, lang = lang, inflect_word_spec = conjugate_verb, -- Return the variant code that was added to ensure only parallel variants in -- multiword expressions like हिलना-डुलना get generated. See com.add_variant_codes -- for more information. get_variants = alternant_multiword_spec.multiword and com.get_variants or nil, -- We add links around the generated verbal forms rather than allow the entire multiword -- expression to be a link, so ensure that user-specified links get included as well. include_user_specified_links = true, }	iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) compute_categories_and_annotation(alternant_multiword_spec) return alternant_multiword_spec end

-- Entry point for. Template-callable function to parse and conjugate a verb given -- user-specified arguments and generate a displayable table of the conjugated forms. function export.show(frame) local parent_args = frame:getParent.args local alternant_multiword_spec = export.do_generate_forms(parent_args) show_forms(alternant_multiword_spec) return make_table(alternant_multiword_spec) .. require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang) end

-- Concatenate all forms of all slots into a single string of the form -- "SLOT=FORM,FORM,...|SLOT=FORM,FORM,...|...". Each FORM is either a string in Devanagari or -- (if manual translit is present) a specification of the form "FORM//TRANSLIT" where FORM is the -- Devanagari representation of the form and TRANSLIT its manual transliteration. Embedded pipe symbols -- (as might occur in embedded links) are converted to <!>. If INCLUDE_PROPS is given, also include -- additional properties (currently, none). This is for use by bots. local function concat_forms(alternant_spec, include_props) local ins_text = {} for slot, _ in pairs(verb_slots_with_linked) do		local formtext = iut.concat_forms_in_slot(alternant_spec.forms[slot]) if formtext then table.insert(ins_text, slot .. "=" .. formtext) end end return table.concat(ins_text, "|") end

-- Template-callable function to parse and conjugate a verb given user-specified arguments and return -- the forms as a string of the same form as documented in concat_forms above. function export.generate_forms(frame) local include_props = frame.args["include_props"] local parent_args = frame:getParent.args local alternant_spec = export.do_generate_forms(parent_args) return concat_forms(alternant_spec, include_props) end

return export